package com.xd.core.api.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.XML;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xd.common.config.RabbitConfig;
import com.xd.common.constant.SysConstant;
import com.xd.common.exception.XkdException;
import com.xd.common.util.AESUtil;
import com.xd.common.util.OrderNumUtil;
import com.xd.common.util.RSAUtils;
import com.xd.core.api.dto.*;
import com.xd.core.api.entity.*;
import com.xd.core.api.mapper.AdvancePaymentOrderMapper;
import com.xd.core.api.mapper.CardProtocolInfoMapper;
import com.xd.core.api.mapper.ChannelPayTowMapper;
import com.xd.core.api.mapper.RepaymentOrderInfoMapper;
import com.xd.core.api.request.TransReqBF0040001;
import com.xd.core.api.response.TransConstant;
import com.xd.core.api.response.TransContent;
import com.xd.core.api.response.TransRespBF0040001;
import com.xd.core.api.rsa.RsaCodingUtil;
import com.xd.core.api.rsa.SignatureUtils;
import com.xd.core.api.service.DLApiService;
import com.xd.core.api.util.*;
import com.xd.core.api.vo.BalanceVo;
import com.xd.core.business.entity.*;
import com.xd.core.business.mapper.*;
import com.xd.core.business.service.IOutInfoService;
import com.xd.core.company.entity.Company;
import com.xd.core.company.entity.CompanyAccount;
import com.xd.core.company.mapper.CompanyAccountMapper;
import com.xd.core.company.mapper.CompanyMapper;
import com.xd.core.company.service.ICompanyService;
import com.xd.core.debt.entity.Debt;
import com.xd.core.debt.mapper.DebtMapper;
import com.xd.core.finance.entity.ConsumeDetail;
import com.xd.core.finance.mapper.ConsumeDetailMapper;
import com.xd.core.h5.dto.AdvancePaymentDto;
import com.xd.core.note.IAsyncService;
import com.xd.core.note.eventEntity.DebtEvent;
import com.xd.core.product.entity.Product;
import com.xd.core.product.mapper.ProductMapper;
import com.xd.core.system.service.MsmService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;


@Slf4j
@Service
public class DLApiServiceImpl implements DLApiService {
    @Value("${aes.key}")
    private String aesKey;
    @Value("${spring.profiles.active}")
    private String active;
    //    @Value("${aes.passKey}")
//    private String passKey;
    @Value("${baofush.baseUrl0040001}")
    private String baseUrl0040001;
    @Value("${baofush.offlinePayNotify}")
    private String offlinePayNotify;
    @Value("${baofush.singlePayReturn}")
    private String singlePayReturn;
    @Value("${baofush.sharenotify}")
    private String sharenotify;
    @Value("${baofush.baodansinglePayReturn}")
    private String baodansinglePayReturn;
    @Value("${baofush.baodansharenotify}")
    private String baodansharenotify;
    @Value("${baofush.offlineSplitNotify}")
    private String offlineSplitNotify;
    @Value("${baofush.xieyiurl}")
    private String xieyiurl;
    @Value("${baofush.zhuanzhang}")
    private String zhuanzhang;
    @Autowired
    private OutInfoMapper outInfoMapper;
    @Autowired
    private IAsyncService iAsyncService;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    @Autowired
    private CompanyAccountMapper companyAccountMapper;
    @Autowired
    private CardProtocolInfoMapper cardProtocolInfoMapper;
    @Autowired
    private RepaymentScheduleMapper repaymentScheduleMapper;
    @Autowired
    private DebtMapper debtMapper;
    @Autowired
    private IncomingInfoMapper incomingInfoMapper;
    @Autowired
    private RepaymentOrderInfoMapper repaymentOrderInfoMapper;
    @Autowired
    private CreditMapper creditMapper;
    @Autowired
    private OutOrderInfoMapper outOrderInfoMapper;
    @Autowired
    private ChannelPayTowMapper channelPayTowMapper;
    @Autowired
    private IOutInfoService iOutInfoService;
    @Autowired
    private ICompanyService iCompanyService;
    @Autowired
    private ConsumeDetailMapper consumeDetailMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private MsmService msmService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private AdvancePaymentOrderMapper advancePaymentOrderMapper;
    @Autowired
    private CarCreditMapper carCreditMapper;
    @Autowired
    private GuaranteeSlipMapper guaranteeSlipMapper;

    //宝付商户使用
    @Value("${baofush.out_member_id}")//放款通道一
    private String out_member_id;
    @Value("${baofush.tow_out_member_id}")//放款通道二
    private String tow_out_member_id;
    @Value("${baofush.out_terminal_id}")//放款通道一
    private String out_terminal_id;
    @Value("${baofush.tow_out_terminal_id}")//放款通道二
    private String tow_out_terminal_id;
    @Value("${baofush.in_member_id}")//回款通道一
    private String in_member_id;
    @Value("${baofush.tow_in_member_id}")//回款通道二
    private String tow_in_member_id;
    @Value("${baofush.in_terminal_id}")//回款通道一
    private String in_terminal_id;
    @Value("${baofush.tow_in_terminal_id}")//回款通道二
    private String tow_in_terminal_id;
    @Value("${baofush.loans_in_account_offline}")//放款线下打款收款账户
    private String loans_in_account_offline;
    @Value("${baofush.tow_loans_in_account_offline}")//线下打款收款账户
    private String tow_loans_in_account_offline;
    @Value("${baofush.pfxPathPassword}")//私钥密码
    private String pfxPathPassword;
    //    private final static String baofucerPath = System.getProperty("user.dir") + "/resources/BAOFUP20240612_pub.cer";
    private final static String baofucerPath = DLApiServiceImpl.class.getClassLoader().getResource("baofupublic.cer").getPath();
    //    private final static String baopriPath = System.getProperty("user.dir") + "/resources/BAOFU20240612_pri.pfx";
    private final static String baopriPath = DLApiServiceImpl.class.getClassLoader().getResource("baofuprivate.pfx").getPath();
    @Autowired
    private CompanyMapper companyMapper;
    private static final int maxRetry = 10;//最大尝试机制
    private static final int sleepMillis = 1000;//休眠时间
    private static final String towBaofuwithdrawLock = "towBaofuwithdrawLock_";//渠道二提现
    private static final String ADVANCE_LOCK_KEY = "lock_advance";//预付款分布式锁

    @Autowired
    private ProductMapper productMapper;


    //代付放款
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void outAmount(OutAmountDto outAmountDto) throws Exception {
        log.info("执行放款");
        String productType = incomingInfoMapper.getProductType(outAmountDto.getIncomingId());
        String dataType = TransConstant.data_type_json; // 数据类型 xml/json
        TransContent<TransReqBF0040001> transContent = new TransContent<TransReqBF0040001>(dataType);
        List<TransReqBF0040001> trans_reqDatas = new ArrayList<TransReqBF0040001>();
        TransReqBF0040001 transReqData = new TransReqBF0040001();
        transReqData.setTrans_no(outAmountDto.getOrder());
        transReqData.setTrans_money(outAmountDto.getTransMoney());//元
        transReqData.setTo_acc_name(outAmountDto.getToAccName());
        transReqData.setTo_acc_no(outAmountDto.getToAccNo());
        transReqData.setTo_bank_name(outAmountDto.getToBankName());
//        transReqData.setTo_pro_name("");
//        transReqData.setTo_city_name("");
//        transReqData.setTo_acc_dept("");
//        transReqData.setTrans_summary("测试摘要");
        if (productType.equals(SysConstant.PRODUCT_FIVE)) {
            transReqData.setTrans_cnap(outAmountDto.getTransCnap());
        } else {
            transReqData.setTrans_card_id(outAmountDto.getTransCardId());
        }
//        transReqData.setTrans_mobile("15821788630");
        trans_reqDatas.add(transReqData);
        transContent.setTrans_reqDatas(trans_reqDatas);

        log.info("订单号：" + transReqData.getTrans_no());
        String bean2XmlString = transContent.obj2Str(transContent);
        log.info("报文：" + bean2XmlString);

        String origData = bean2XmlString;

        origData = SecurityUtil.Base64Encode(origData);
//        String DataContent = RsaCodingUtil.encryptByPriPfxFile(origData, companyAccount.getOutPriPath(), AESUtil.decrypt(companyAccount.getOutPriPw(), passKey));
        String DataContent = RsaCodingUtil.encryptByPriPfxFile(origData, baopriPath, pfxPathPassword);
        log.info("----------->【私钥加密-结果】" + DataContent);

        String member_id = null;
        String terminal_id = null;
        if (outAmountDto.getChannelType().equals(SysConstant.CHANNEL_TYPE_ONE)) {
            member_id = out_member_id;
            terminal_id = out_terminal_id;
        } else if (outAmountDto.getChannelType().equals(SysConstant.CHANNEL_TYPE_TWO)) {
            member_id = tow_out_member_id;
            terminal_id = tow_out_terminal_id;
        }
        //发送请求参数
        Map<String, String> PostArry = new TreeMap<String, String>();
        PostArry.put("version", "4.0.0");
        PostArry.put("member_id", member_id);
        PostArry.put("terminal_id", terminal_id);
        PostArry.put("data_type", dataType);
        PostArry.put("data_content", DataContent);

        String ReslutString = HttpUtil.RequestForm(baseUrl0040001, PostArry);
        log.info("请求返回:" + ReslutString);
        TransContent<TransRespBF0040001> str2Obj = new TransContent<TransRespBF0040001>(dataType);

        //明文返回处理可能是报文头参数不正确、或其他的异常导致；
        if (ReslutString.contains("trans_content")) {
            //明文返回
            //我报文错误处理
            str2Obj = (TransContent<TransRespBF0040001>) str2Obj.str2Obj(ReslutString, TransRespBF0040001.class);
            // 业务逻辑判断
        } else {
            //密文返回
            //第一步：公钥解密
//            ReslutString = RsaCodingUtil.decryptByPubCerFile(ReslutString, companyAccount.getOutPubPath());
            ReslutString = RsaCodingUtil.decryptByPubCerFile(ReslutString, baofucerPath);
            log.info("解密RSA：" + ReslutString);
            //第二步BASE64解密
            ReslutString = SecurityUtil.Base64Decode(ReslutString);
            log.info("解密后明文:" + ReslutString);
            str2Obj = (TransContent<TransRespBF0040001>) str2Obj.str2Obj(ReslutString, TransRespBF0040001.class);
            // 业务逻辑判断
        }
        String returnCode = str2Obj.getTrans_head().getReturn_code();
        String returnMsg = str2Obj.getTrans_head().getReturn_msg();
        if (!returnCode.equals("0000")) {
            OutInfo outInfo = new OutInfo();
            outInfo.setOutStatus("3");
            outInfo.setTransOrderId(outAmountDto.getOrder());
            LambdaQueryWrapper<OutInfo> lqw = new LambdaQueryWrapper<>();
            lqw.eq(OutInfo::getOutId, outAmountDto.getId());
            //修改放款状态
            outInfoMapper.update(outInfo, lqw);
            //修改放款订单状态
            if (outAmountDto.getChannelType().equals(SysConstant.CHANNEL_TYPE_ONE)) {//渠道一
                OutOrderInfo o = new OutOrderInfo();
                o.setPayStatus("3");
                o.setFailReason(returnMsg);
                LambdaQueryWrapper<OutOrderInfo> lqw2 = new LambdaQueryWrapper<>();
                lqw2.eq(OutOrderInfo::getOrderNo, outAmountDto.getOrder());
                outOrderInfoMapper.update(o, lqw2);
            } else if (outAmountDto.getChannelType().equals(SysConstant.CHANNEL_TYPE_TWO)) {//渠道二
                ChannelPayTow channelPayTow = new ChannelPayTow();
                channelPayTow.setPayStatus("3");
                channelPayTow.setFailReason(returnMsg);
                LambdaQueryWrapper<ChannelPayTow> lqw3 = new LambdaQueryWrapper<>();
                lqw3.eq(ChannelPayTow::getOrderNo, outAmountDto.getOrder());
                channelPayTowMapper.update(channelPayTow, lqw3);
                //如果是第三方
                String incomingType = incomingInfoMapper.selectById(outAmountDto.getIncomingId()).getIncomingType();
                if (incomingType.equals("2"))
                    iAsyncService.advanceOutAmount(outAmountDto.getOrder(), outAmountDto.getCompanyId(), "3", "申请放款失败，请联系管理员", null);

            }
            log.info("放款失败");
        }
        if (outAmountDto.getChannelType().equals(SysConstant.CHANNEL_TYPE_TWO)) {//渠道二
            //冻结宝付余额
            Long multiply = new BigDecimal(outAmountDto.getTransMoney()).multiply(new BigDecimal("100")).longValue();
            companyMapper.freezeBaofuBalance(outAmountDto.getCompanyId(), multiply);
        }
        log.info("申请放款执行完毕");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void outAmountReturn(Map<String, Object> fpnre) throws IOException {
        log.info("执行放款回调函数");
        if (fpnre == null)
            throw new XkdException("Parms is Null");
        log.info("访问入参：" + fpnre);
        String resultJson = JSON.toJSONString(fpnre);
        FoPayNotifyRsaEntity result = JSON.parseObject(resultJson, FoPayNotifyRsaEntity.class);
        log.info("转对象：" + result);
        if (result == null) throw new XkdException("对象转换异常");
        if (result.getData_content() == null) throw new XkdException("数据域为空");
        if (result.getData_content().equals("null") || StringUtils.isBlank(result.getData_content()))
            throw new XkdException("数据域为空");
        String ReslutString = RsaCodingUtil.decryptByPubCerFile(result.getData_content(), baofucerPath);
        log.info("解密RSA：" + ReslutString);
        //第二步BASE64解密
        ReslutString = SecurityUtil.Base64Decode(ReslutString);
        log.info("BASE64解密:" + ReslutString);
        /***
         * 商户端处理
         */
        JSONObject jsonObject = XML.toJSONObject(ReslutString);
        JSONObject trans_content = jsonObject.getJSONObject("trans_content");
        JSONObject trans_reqDatas = trans_content.getJSONObject("trans_reqDatas");
        JSONObject trans_reqData = trans_reqDatas.getJSONObject("trans_reqData");
        String transOrderid = trans_reqData.getStr("trans_orderid");
        log.info("代付回调宝付订单号：" + transOrderid);
        String transNo = trans_reqData.getStr("trans_no");//放款商户订单
        String state = trans_reqData.getStr("state");
        String trans_remark = trans_reqData.getStr("trans_remark");
        String trans_endtime = trans_reqData.getStr("trans_endtime");
        log.info("代付回调商户订单号：" + transNo);

        OutOrderInfo outOrderInfo = null;
        for (int i = 1; i <= maxRetry; i++) {
            outOrderInfo = outOrderInfoMapper.selectOne(new LambdaQueryWrapper<OutOrderInfo>().eq(OutOrderInfo::getOrderNo, transNo));
            log.info("第 {} 次查询放款订单数据：{}", i, outOrderInfo);
            if (outOrderInfo != null)
                break;
            try {
                Thread.sleep(sleepMillis);
            } catch (InterruptedException e) {
                log.warn("等待过程中被中断", e);
                Thread.currentThread().interrupt(); // 恢复中断状态
                break;
            }
        }
        if (outOrderInfo == null) {
            log.error("重试 {} 次后仍未获取到放款订单数据", maxRetry);
            throw new RuntimeException("未获取到放款订单数据，请稍后再试");
        }

        if (!outOrderInfo.getPayStatus().equals("1")) {//如果不是申请中的状态直接返回 避免重复回调
            return;
        }

        OutInfo outInfo = new OutInfo();
        OutOrderInfo o = new OutOrderInfo();
        Integer incomingId = outOrderInfo.getIncomingId();
        String outStatus = "";
        String status = "";
        String payStatus = "";
        IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
        Product product = productMapper.selectById(incomingInfo.getProductId());
        if (state.equals("1")) {//放款成功
            outInfo.setPayTime(new Date());
            outInfo.setOrderNo(transNo);
            outStatus = "2";
            status = SysConstant.BUSINESS_STATUS_THIRTEEN;
            payStatus = "2";
            Credit credit = creditMapper.selectByCredit(incomingId);
            OutInfo outInfo1 = outInfoMapper.selectById(outOrderInfo.getOrderId());
            //保单贷：：入保单
            if (product.getType().equals(SysConstant.PRODUCT_FIVE)) {
                iOutInfoService.calculateInsuranceLoanSchedule(outInfo1,credit.getPostServiceFeeRate());
                List<CarCredit> carCredits = carCreditMapper.selectList(new LambdaQueryWrapper<CarCredit>()
                        .eq(CarCredit::getIncomingId, incomingId)
                        .ge(CarCredit::getEndTime, new Date()));
                List<GuaranteeSlip> guaranteeSlips = BeanUtil.copyToList(carCredits, GuaranteeSlip.class);
                if (CollectionUtil.isNotEmpty(guaranteeSlips)) {
                    guaranteeSlipMapper.batchInsert(guaranteeSlips);
                }
            } else {
                iOutInfoService.calculateLoanSchedule(outInfo1);
            }
            String incomingOrder = incomingInfo.getIncomingOrder();
            //扣除放款服务费
            iCompanyService.deductingBalanceMain(outInfo1, incomingOrder, incomingId, null, SysConstant.BUSINESS_FEE_FIVE, null, null, null);
            try {
                //扣除产品服务费
                iCompanyService.deductingBalanceMain(outInfo1, null, incomingId, null, SysConstant.BUSINESS_FEE_ONE, null, null, null);
            } catch (Exception e) {
                log.info("放款回调，放款金额未在设置阶梯收费范围内");
            }
            //扣除合同服务费
            iCompanyService.deductingBalanceMain(null, null, incomingId, null, SysConstant.BUSINESS_FEE_SEVEN, null, null, null);
//                        String controversy = outInfo1.getControversy();
//            if (SysConstant.CONTROVERSY_ONE.equals(controversy)){
//                //扣除公证书服务费
//                iCompanyService.deductingBalanceMain(outInfo1,incomingOrder,incomingId,null,SysConstant.BUSINESS_FEE_THREE,null,null,null);
//            }
            //修改宝付余额
            companyMapper.updateBaofuBalance(outOrderInfo.getCompanyId(), outOrderInfo.getAmount());
            //发送短信
            String format = sendText(outInfo1, incomingInfo.getName(), credit);


            try {
                if (!product.getType().equals("4")) {
                    log.info("发送放款到账短信");
                    msmService.sendH5(AESUtil.decrypt(incomingInfo.getPhone(), aesKey), format, format);
                }
            } catch (Exception e) {
                log.info("发送放款到账短信失败");
            }
        } else if (state.equals("-1")) {//支付失败
            outStatus = "3";
            status = SysConstant.BUSINESS_STATUS_TWELVE;
            payStatus = "3";
        } else if (state.equals("2")) {//退款
            outStatus = "4";
            payStatus = "4";
            status = SysConstant.BUSINESS_STATUS_TWELVE;
        }
        outInfo.setOutStatus(outStatus);
        outInfo.setTransOrderId(transOrderid);
        LambdaQueryWrapper<OutInfo> lqw = new LambdaQueryWrapper<>();
        lqw.eq(OutInfo::getOutId, outOrderInfo.getOrderId());

        //修改放款订单状态
        o.setTradeTime(DateUtil.parse(trans_endtime));
        o.setPayStatus(payStatus);
        o.setFailReason(trans_remark);
        LambdaQueryWrapper<OutOrderInfo> lqw2 = new LambdaQueryWrapper<>();
        lqw2.eq(OutOrderInfo::getOrderNo, transNo);
        outOrderInfoMapper.update(o, lqw2);
        if (product.getType().equals(SysConstant.PRODUCT_FIVE)) {//除了保单贷，其他进件流程只到授信完成
            incomingInfoMapper.updateStatus(status, incomingId);
        } else {
            outInfo.setStatus(status);
        }
        //修改放款状态
        outInfoMapper.update(outInfo, lqw);


        log.info("放款回调函数执行完毕");
    }

    public String sendText(OutInfo outInfo, String name, Credit credit) {
        //发送短信
        String content = "【宝坤网络小额贷】尊敬的{}先生/女士，您的贷款已发放至指定账户，请注意査收。为确保后续还款顺利，请于每月{}日前确保您尾号{}的{}账户余额充足或及时主动还款;如有疑问，请联系客户经理，感谢您的信任与配合!";
        String loanMethod = outInfo.getLoanMethod();
        String repayDay = outInfo.getRepayDay();
        if (SysConstant.REPAY_TYPE_THREE.equals(loanMethod)) {
            //一次性还本付清
            content = "【宝坤网络小额贷】尊敬的{}先生/女士，您的贷款已发放至指定账户，请注意査收。为确保后续还款顺利，请于{}前确保您尾号{}的{}账户余额充足或及时主动还款;如有疑问，请联系客户经理，感谢您的信任与配合!";
            repayDay = DateUtil.format(outInfo.getOutEndTime(), "yyyy年MM月dd日");
        } else if (SysConstant.REPAY_FREQUENCY_WEEK.equals(outInfo.getRepayFrequency())) {
            //按周还款
            content = "【宝坤网络小额贷】尊敬的{}先生/女士，您的贷款已发放至指定账户，请注意査收。为确保后续还款顺利，请于每周周{}确保您尾号{}的{}账户余额充足或及时主动还款;如有疑问，请联系客户经理，感谢您的信任与配合!";
            switch (repayDay) {
                case "1":
                    repayDay = "一";
                    break;
                case "2":
                    repayDay = "二";
                    break;
                case "3":
                    repayDay = "三";
                    break;
                case "4":
                    repayDay = "四";
                    break;
                case "5":
                    repayDay = "五";
                    break;
                case "6":
                    repayDay = "六";
                    break;
                case "7":
                    repayDay = "日";
                    break;
            }
        }
        return StrFormatter.format(content, name, repayDay, credit.getRepayAccount().substring(credit.getRepayAccount().length() - 4), credit.getRepayBank());
    }

    public static void main(String[] args) {
        String data = "GQVLxCewxVQvVrhWysbOMOhHVtSzBddZxP9FKEBTDlhsLJI2U/vo0TKuRkZNM9ilYfWsEa3MdggCAqrmwyb96uiLeRLI9DF2w0ORHRLbQT+S6LgKEkSkSm5BjQ5QyJ1AfkvCNm2hGf/7cpbEzHAk4aTndKdcIlXNirheSJMvOjjGVFWnUN02utAOBoUDngu+F+MwH8TqqqdjPdqtq92eZ4xr43NRklEn7YPZMUv+8h7BX6+FanR92KXJFekl3MnafXGLmWl4j+cCakaRITShEHZj196xRgx0pyZszsqwIYgW5zzblmkLQZCaEcP/6ykA9Kdt+YZ6ovJU4gNYZUj9IA==";
        ReadySignDto readySignDto = com.alibaba.fastjson.JSONObject.parseObject(RSAUtils.privateDecrypt(data, "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDlGf2Bothxcfd0PI2sRZvDCxfEBxWZf7FuAPXlDjZRQZXmRdfzh9GUmCNNF3wencQc2atxe/yjBfwnNTmgwXMo2zP1oaMet4f32vi3fJRzO3aD9267AkvZbh4IEUHAJTn2B0bf+d2fLhfRV+5TtPiPOlrifNTfBmMv1tyiK5tSZOQQubsLNLzogn8U8obFJBBgzuW1HQXjXWltImdxqPGPE7oZp+OypCYF0XT5QQR1+33zwxpM/kw95gty6pgaqWdh8rOQJTitr+qEn1xE2zuZWJ1OMtCrkMDG/eWeqp73EHMLWbOd63vBP3AY3/ERGzhJ8PLLMS6cG1y5oFnr5tTfAgMBAAECggEAT4F7A/uXAJur5BdFlTTc7s2RNFK/ELOLSCEmNJT/jahklHLFlDqcpWQ8z/qn9NoMXnS9thbwoESp8pNaierIg7O6rvImsTjBA2dm7ApcXDcVFIwQTe7okq7P1j3i4wMoP7TbyEumuSCeXGSXDU6e+dmnA7WNjE5jqoz2qYLXlrpLceaYzHI2yrpP61xr3rbURzR0sdfCmjI5g4dVUWPxeD8e3JiQ+gONziVDi71cioMEfLmDB7yLlZ5MKwxpkC/0U0Z3yXpKtB+j7mU/JaPz5J4FBubiB6Sdj/PROuxlCv/EkSbpuVMmZKyVvMx4QnuvG3GOL8zms77RmLB2ewS1eQKBgQD4/RLPpauznK+++BHvtfo6mEhxq3ZT/DS4l71MEDfhK5XE6mChfAjVDG6z7pHGy4rToVUbg6yAnofOs4AtOja7t/zX3NLVaIfC3J4lzDgRO1tjRyvpz01pyyRNfKbIIuk9ZYNt8+TFkSi3fmFIloDYog6wdqoU9O7Aj8j5sfromQKBgQDrjY27aAXRNsjglwXvTIAj6Rpblh1lMx8r5dYEanueOQ/IfY2aGsanX2i2Qiv+QbmnlYVWVo75PEh/sb0hc/Z3bCI6f7IJ9e2J8tUWw7PHZ2fgE4iJmUy4JP3ru9wvTrLvSaUvrykNGNzA/wIX5YeZAlW8+g6TsyUNrJKyGjM8NwKBgQD4JSgxQDhuKjNRDOS7VU7MROa2Op8UNp/IdJnZC7TVOV3rQXcKmZYn1lsiAhrQ2BxErF4orMMn8FcDHwwYhxLzZPudmtuYirP69Wq72trp+ZEr0Uunms3ZKPbGLvv9LCXt5ZTl6RNP/0poDZEhCbXhsSkYnzsE5dJooSQ4DjQ0yQKBgQCM9Q5loWApzHD1BSsGAm+EDdA6EmDQ2IDE0ir6RYZoLpllTszX9UH/krakqoCNDGO/B1jdS7sPJqXz1Dbrt+s7l7f77cJq94IIVQpzLvV8VELy8n14agZhA7H3yVl/7cZiL13sZRNZxREFZRSpnC9CvwTzq2fhtwhrYGQAtHEIjQKBgQC4Pb0KOMEyWpl3iYHg/2DX5cH+TPqc1VO6M2QJNxw3HbBv8vr03F1cG35kuhWWZoUxREEQpr4t/WNcBy2SDhcsaPwCsCyaqM5soBrWDxI+3oBIm311XtLXXbcehqZX2wPQ2cuoI0s1AYwfww0IJfJ99jBiD2T+knHl/NvkWh//7A=="), ReadySignDto.class);
        System.out.println(readySignDto);
    }
    @Override
    public String bindBankCard(String repayPhone, String repayAccount, String repayName, String repayIdCard) throws Exception {

        String send_time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//报文发送日期时间
        String MemberId = in_member_id;//商户号
        String TerminalId = in_terminal_id;//终端号

        String pfxpath = baopriPath;//商户私钥
        String cerpath = baofucerPath;//宝付公钥
        String pfxpwd = pfxPathPassword;//私钥密码

//        String Cardinfo = readySignDto.getRepayAccount() + "|" + readySignDto.getRepayName() + "|" + readySignDto.getRepayIdCard() + "|" + phone + "||";
        String Cardinfo = repayAccount + "|" + repayName + "|" + repayIdCard + "|" + repayPhone;

        String AesKey = FormatUtil.CreateAeskey();//商户自定义（可随机生成  商户自定义(AES key长度为=16位)）
        String dgtl_envlp = "01|" + AesKey;//使用接收方的公钥加密后的对称密钥，并做Base64转码，明文01|对称密钥，01代表AES[密码商户自定义]
        log.info("密码dgtl_envlp：" + dgtl_envlp);
        dgtl_envlp = RsaCodingUtil.encryptByPubCerFile(SecurityUtil.Base64Encode(dgtl_envlp), cerpath);//公钥加密

        Cardinfo = SecurityUtil.AesEncrypt(SecurityUtil.Base64Encode(Cardinfo), AesKey);//先BASE64后进行AES加密
        log.info("AES结果:" + Cardinfo);

        Map<String, String> DateArry = new TreeMap<String, String>();
        DateArry.put("send_time", send_time);
        DateArry.put("msg_id", "FMXC" + FormatUtil.CreateAeskey(28));//报文流水号
        DateArry.put("version", "4.0.0.0");
        DateArry.put("terminal_id", TerminalId);
        DateArry.put("txn_type", "01");//交易类型
        DateArry.put("member_id", MemberId);
        DateArry.put("dgtl_envlp", dgtl_envlp);
        DateArry.put("user_id", "");
        DateArry.put("card_type", "101");//卡类型  101	借记卡，102 信用卡
        DateArry.put("id_card_type", "01");//证件类型
        DateArry.put("acc_info", Cardinfo);

        String SignVStr = FormatUtil.coverMap2String(DateArry);
        log.info("SHA-1摘要字串：" + SignVStr);
        String signature = SecurityUtil.sha1X16(SignVStr, "UTF-8");//签名
        log.info("SHA-1摘要结果：" + signature);

        String Sign = SignatureUtils.encryptByRSA(signature, pfxpath, pfxpwd);
        log.info("RSA签名结果：" + Sign);
        DateArry.put("signature", Sign);//签名域

        String PostString = HttpUtil.RequestForm(xieyiurl, DateArry);
        log.info("请求返回:" + PostString);

        Map<String, String> ReturnData = FormatUtil.getParm(PostString);
        if (!ReturnData.containsKey("signature")) {
            throw new Exception("缺少验签参数！");
        }

        String RSign = ReturnData.get("signature");
        log.info("返回的验签值：" + RSign);
        ReturnData.remove("signature");//需要删除签名字段
        String RSignVStr = FormatUtil.coverMap2String(ReturnData);
        log.info("返回SHA-1摘要字串：" + RSignVStr);
        String RSignature = SecurityUtil.sha1X16(RSignVStr, "UTF-8");//签名
        log.info("返回SHA-1摘要结果：" + RSignature);

        if (SignatureUtils.verifySignature(cerpath, RSignature, RSign)) {
            log.info("Yes");//验签成功


            if (!ReturnData.containsKey("resp_code")) {
                throw new Exception("缺少resp_code参数！");
            }

            if (ReturnData.get("resp_code").toString().equals("S")) {
                if (!ReturnData.containsKey("dgtl_envlp")) {
                    throw new Exception("缺少dgtl_envlp参数！");
                }
                String RDgtlEnvlp = SecurityUtil.Base64Decode(RsaCodingUtil.decryptByPriPfxFile(ReturnData.get("dgtl_envlp"), pfxpath, pfxpwd));
                log.info("返回数字信封：" + RDgtlEnvlp);
                String RAesKey = FormatUtil.getAesKey(RDgtlEnvlp);//获取返回的AESkey
                log.info("返回的AESkey:" + RAesKey);
                String code = SecurityUtil.Base64Decode(SecurityUtil.AesDecrypt(ReturnData.get("unique_code"), RAesKey));
                log.info("唯一码:" + code);
                return code;
            }
            throw new XkdException(ReturnData.get("biz_resp_msg"));

        }
        return null;
    }

    @Override
    public String readySign(ReadySignDto readySignDto) throws Exception {
        //查询银行卡是否已经绑定
        CardProtocolInfo cardProtocolInfo = cardProtocolInfoMapper.selectOne(new LambdaQueryWrapper<CardProtocolInfo>().eq(CardProtocolInfo::getRepayAccount, readySignDto.getRepayAccount()));
        if (cardProtocolInfo != null)
            throw new XkdException("该银行卡已绑定");
        String phone = AESUtil.decrypt(readySignDto.getRepayPhone(), aesKey);
        return bindBankCard(phone, readySignDto.getRepayAccount(), readySignDto.getRepayName(), readySignDto.getRepayIdCard());
    }

    @Override
    public void affirmBindCard(Integer creditId, String smsCode, String uniqueCode, String repayAccount, String repayName, String repayIdCard, String repayPhone,String repayBank) throws Exception {
        String send_time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//报文发送日期时间
        String MemberId = in_member_id;//商户号
        String TerminalId = in_terminal_id;//终端号

        String pfxpath = baopriPath;//商户私钥
        String cerpath = baofucerPath;//宝付公钥
        String pfxpwd = pfxPathPassword;//私钥密码

        String SMSStr = smsCode;//短信验证码，测试环境随机6位数;生产环境验证码预绑卡成功后发到用户手机。确认绑卡时回传。

        String AesKey = FormatUtil.CreateAeskey();//商户自定义（可随机生成  商户自定义(AES key长度为=16位)）
        String dgtl_envlp = "01|" + AesKey;//使用接收方的公钥加密后的对称密钥，并做Base64转码，明文01|对称密钥，01代表AES[密码商户自定义]

        log.info("密码dgtl_envlp：" + dgtl_envlp);
        dgtl_envlp = RsaCodingUtil.encryptByPubCerFile(SecurityUtil.Base64Encode(dgtl_envlp), cerpath);//公钥加密
        String UniqueCode = uniqueCode + "|" + SMSStr;//预签约唯一码(预绑卡返回的值)[格式：预签约唯一码|短信验证码]
        log.info("预签约唯一码：" + UniqueCode);
        UniqueCode = SecurityUtil.AesEncrypt(SecurityUtil.Base64Encode(UniqueCode), AesKey);//先BASE64后进行AES加密
        log.info("AES结果:" + UniqueCode);

        Map<String, String> DateArry = new TreeMap<String, String>();
        DateArry.put("send_time", send_time);
        DateArry.put("msg_id", "FMXC" + FormatUtil.CreateAeskey(28));//报文流水号
        DateArry.put("version", "4.0.0.0");
        DateArry.put("terminal_id", TerminalId);
        DateArry.put("txn_type", "02");//交易类型
        DateArry.put("member_id", MemberId);
        DateArry.put("dgtl_envlp", dgtl_envlp);
        DateArry.put("unique_code", UniqueCode);//预签约唯一码

        String SignVStr = FormatUtil.coverMap2String(DateArry);
        log.info("SHA-1摘要字串：" + SignVStr);
        String signature = SecurityUtil.sha1X16(SignVStr, "UTF-8");//签名
        log.info("SHA-1摘要结果：" + signature);
        String Sign = SignatureUtils.encryptByRSA(signature, pfxpath, pfxpwd);
        log.info("RSA签名结果：" + Sign);
        DateArry.put("signature", Sign);//签名域
        String PostString = HttpUtil.RequestForm(xieyiurl, DateArry);
        log.info("请求返回:" + PostString);

        Map<String, String> ReturnData = FormatUtil.getParm(PostString);

        if (!ReturnData.containsKey("signature")) {
            throw new Exception("缺少验签参数！");
        }

        String RSign = ReturnData.get("signature");
        log.info("返回的验签值：" + RSign);
        ReturnData.remove("signature");//需要删除签名字段
        String RSignVStr = FormatUtil.coverMap2String(ReturnData);
        log.info("返回SHA-1摘要字串：" + RSignVStr);
        String RSignature = SecurityUtil.sha1X16(RSignVStr, "UTF-8");//签名
        log.info("返回SHA-1摘要结果：" + RSignature);

        if (SignatureUtils.verifySignature(cerpath, RSignature, RSign)) {
            log.info("Yes");//验签成功
        } else {
            throw new XkdException("验签失败");
        }
        if (!ReturnData.containsKey("resp_code")) {
            throw new Exception("缺少resp_code参数！");
        }
        if (ReturnData.get("resp_code").toString().equals("S")) {
            if (!ReturnData.containsKey("dgtl_envlp")) {
                throw new Exception("缺少dgtl_envlp参数！");
            }
            String RDgtlEnvlp = SecurityUtil.Base64Decode(RsaCodingUtil.decryptByPriPfxFile(ReturnData.get("dgtl_envlp"), pfxpath, pfxpwd));
            log.info("返回数字信封：" + RDgtlEnvlp);
            String RAesKey = FormatUtil.getAesKey(RDgtlEnvlp);//获取返回的AESkey
            log.info("返回的AESkey:" + RAesKey);
            String protocol_no = SecurityUtil.Base64Decode(SecurityUtil.AesDecrypt(ReturnData.get("protocol_no"), RAesKey));
            log.info("签约协议号:" + protocol_no);
            //更新字段
            creditMapper.updateCardStatus(creditId, SysConstant.USER_CARD_STATUS_YES);
            //插入绑卡信息避免以后重复
            CardProtocolInfo cardProtocolInfo = new CardProtocolInfo();
            cardProtocolInfo.setProtocolNo(protocol_no);
            cardProtocolInfo.setRepayAccount(repayAccount);
            cardProtocolInfo.setRepayName(repayName);
            cardProtocolInfo.setRepayIdCard(repayIdCard);
            cardProtocolInfo.setRepayPhone(repayPhone);
            cardProtocolInfo.setRepayBank(repayBank);
            cardProtocolInfoMapper.insert(cardProtocolInfo);

        } else if (ReturnData.get("resp_code").toString().equals("I")) {
            throw new Exception(ReturnData.get("biz_resp_msg"));
        } else if (ReturnData.get("resp_code").toString().equals("F")) {
            throw new Exception(ReturnData.get("biz_resp_msg"));
        } else {
            throw new Exception(ReturnData.get("biz_resp_msg"));//异常不得做为订单状态。
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void confirmSign(ConfirmSignDto confirmSignDto) throws Exception {
        affirmBindCard(confirmSignDto.getCreditId(),
                confirmSignDto.getSmsCode(),
                confirmSignDto.getUniqueCode(),
                confirmSignDto.getRepayAccount(),
                confirmSignDto.getRepayName(),
                confirmSignDto.getRepayIdCard(),
                confirmSignDto.getRepayPhone(),
                confirmSignDto.getRepayBank());
    }

    //代扣
    @Override
    public void singlePay(SinglePayDto singlePayDto, RepaymentOrderInfo repaymentOrderInfo) throws Exception {

        CompanyAccount companyAccount = companyAccountMapper.selectById(singlePayDto.getCompanyId());
        String send_time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//报文发送日期时间 //send_time="2038-12-23 10:00:00";
        CardProtocolInfo cardProtocolInfo = cardProtocolInfoMapper.selectOne(new LambdaQueryWrapper<CardProtocolInfo>().eq(CardProtocolInfo::getRepayAccount, repaymentOrderInfo.getRepayAccount()));
        if (cardProtocolInfo == null)
            throw new XkdException("已解绑银行卡");
        String ProtocolNo = cardProtocolInfo.getProtocolNo();//签约协议号（确认绑卡返回）
        OutInfo outInfo = outInfoMapper.selectById(repaymentOrderInfo.getOutId());
        String payPass = outInfo.getPayPass();
        String MemberId = null;
        String TerminalId = null;
        String txn_type = null;
        if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {
            MemberId = in_member_id;//商户号
            TerminalId = in_terminal_id;
            txn_type = "18";//分账
        } else if (payPass.equals(SysConstant.CHANNEL_TYPE_TWO)) {
            MemberId = tow_in_member_id;//商户号
            TerminalId = tow_in_terminal_id;
            txn_type = "08";//不分账
        }

        String pfxpath = baopriPath;//商户私钥
        String cerpath = baofucerPath;//宝付公钥
        String pfxpwd = pfxPathPassword;//私钥密码

        String TransId = singlePayDto.getOrderNo();

        log.info("订单号：" + TransId);

        String AesKey = FormatUtil.CreateAeskey();//商户自定义(可随机生成  AES key长度为=16位)
        String dgtl_envlp = "01|" + AesKey;//+AesKey;//使用接收方的公钥加密后的对称密钥，并做Base64转码，明文01|对称密钥，01代表AES[密码商户自定义]
        log.info("密码dgtl_envlp：" + dgtl_envlp);
        dgtl_envlp = RsaCodingUtil.encryptByPubCerFile(SecurityUtil.Base64Encode(dgtl_envlp), cerpath);//公钥加密
        log.info("RSA公钥加密结果" + dgtl_envlp);


        ProtocolNo = SecurityUtil.AesEncrypt(SecurityUtil.Base64Encode(ProtocolNo), AesKey);//先BASE64后进行AES加密
        log.info("签约协议号AES结果:" + ProtocolNo);

        String CardInfo = "";//信用卡：信用卡有效期|安全码,借记卡：传空

        //暂不支持信用卡
        CardInfo = SecurityUtil.AesEncrypt(SecurityUtil.Base64Encode(CardInfo), AesKey);//先BASE64后进行AES加密


        Map<String, String> DateArry = new TreeMap<String, String>();
        DateArry.put("send_time", send_time);
        DateArry.put("msg_id", "FMXC" + FormatUtil.CreateAeskey(28));//报文流水号
        DateArry.put("version", "4.0.0.0");
        DateArry.put("terminal_id", TerminalId);
        DateArry.put("txn_type", txn_type);//交易类型(参看：文档中《交易类型枚举》)
        DateArry.put("member_id", MemberId);
        DateArry.put("trans_id", TransId);
        DateArry.put("dgtl_envlp", dgtl_envlp);
        DateArry.put("user_id", "");//用户在商户平台唯一ID (和绑卡时要一致)
        DateArry.put("protocol_no", ProtocolNo);//签约协议号（密文）
        DateArry.put("txn_amt", singlePayDto.getAmount());//交易金额 [单位：分  例：1元则提交100]，此处注意数据类型的转转，建议使用BigDecimal类弄进行转换为字串
        DateArry.put("card_info", CardInfo);//卡信息
        if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {
            DateArry.put("share_notify_url", sharenotify);//分账异步通知
            //100%分账
            String share_info = companyAccount.getPrestoreMemberId() + "," + singlePayDto.getAmount() + ";";
            DateArry.put("share_info", share_info);//分账
        }
//		DateArry.put("fee_member_id", "");//手续费商户

        Map<String, String> RiskItem = new HashMap<String, String>();
        /**--------风控基础参数-------------**/
        /**
         * 说明风控参数必须，按商户开通行业、真实交易信息传，不可传固定值。
         */
        RiskItem.put("goodsCategory", "02");//商品类目 详见附录《商品类目》 平台为互消金融
        RiskItem.put("chPayIp", "127.0.0.1");//持卡人支付IP
        RiskItem.put("deviceOrderNo", "");//加载设备指纹中的订单号


        DateArry.put("risk_item", net.sf.json.JSONObject.fromObject(RiskItem).toString());//放入风控参数
        DateArry.put("return_url", singlePayReturn);//最多填写三个地址,不同地址用间使用‘|’分隔 //异步通知接收地址

        String SignVStr = FormatUtil.coverMap2String(DateArry);
        log.info("SHA-1摘要字串：" + SignVStr);
        String signature = SecurityUtil.sha1X16(SignVStr, "UTF-8");//签名
        log.info("SHA-1摘要结果：" + signature);
        String Sign = SignatureUtils.encryptByRSA(signature, pfxpath, pfxpwd);
        log.info("RSA签名结果：" + Sign);
        DateArry.put("signature", Sign);//签名域

        String PostString = HttpUtil.RequestForm(xieyiurl, DateArry);
        log.info("请求返回:" + PostString);

        Map<String, String> ReturnData = FormatUtil.getParm(PostString);

        if (!ReturnData.containsKey("signature")) {
            throw new Exception("缺少验签参数！");
        }
        String RSign = ReturnData.get("signature");
        log.info("返回的验签值：" + RSign);
        ReturnData.remove("signature");//需要删除签名字段
        String RSignVStr = FormatUtil.coverMap2String(ReturnData);
        log.info("返回SHA-1摘要字串：" + RSignVStr);
        String RSignature = SecurityUtil.sha1X16(RSignVStr, "UTF-8");//签名
        log.info("返回SHA-1摘要结果：" + RSignature);

        if (SignatureUtils.verifySignature(cerpath, RSignature, RSign)) {
            log.info("验签：Yes");//验签成功

            if (!ReturnData.containsKey("resp_code")) {
                throw new XkdException("缺少resp_code参数！");
            }
            if (ReturnData.get("resp_code").toString().equals("S")) {
                log.info("申请支付成功！");
            } else if (ReturnData.get("resp_code").toString().equals("I")) {
                log.info("交易处理中！");
            } else if (ReturnData.get("resp_code").toString().equals("F")) {
                repaymentOrderInfo.setPayStatus("3");
                repaymentOrderInfo.setFailCause(ReturnData.get("biz_resp_msg").toString());
                signPayFailMessage(repaymentOrderInfo, 5000);
                throw new XkdException("交易失败！" + ReturnData.get("biz_resp_msg").toString());//异常不得做为订单状态。
            } else {
                throw new XkdException("反回异常！");//异常不得做为订单状态。
            }
        } else {
            log.info("验签：NO");
        }
    }

    private void signPayFailMessage(RepaymentOrderInfo msg, int delayMillis) {
        log.info("发送代扣申请失败延迟消息");
        rabbitTemplate.convertAndSend(
                RabbitConfig.DELAY_EXCHANGE,
                RabbitConfig.DELAY_ROUTING_KEY_SIGNPAY,
                msg,
                message -> {
                    message.getMessageProperties().setDelay(delayMillis);
                    return message;
                });
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void singlePayReturn(HttpServletRequest request) throws Exception {
        request.setCharacterEncoding("utf-8");//utf-8编码接收
        RequestParmUtil.getParamLog(request);//打印接收参数

        Map<String, String> RDateArry = new TreeMap<String, String>();
        Enumeration<String> e = request.getParameterNames();
        while (e.hasMoreElements()) {
            String paraStr = e.nextElement();
            if (paraStr != null) {
                RDateArry.put(paraStr, request.getParameter(paraStr));//接收参数,（使用TreeMap自动排序）
            }
        }

        if (!RDateArry.containsKey("signature")) {
            throw new XkdException("异常缺少验签参数！");
        }
        String RSign = RDateArry.get("signature");
        log.info("返回的签名值：" + RSign);
        RDateArry.remove("signature");//需要删除签名字段
        String RSignVStr = FormatUtil.coverMap2String(RDateArry);
        log.info("返回SHA-1摘要字串：" + RSignVStr);
        String RSignature = SecurityUtil.sha1X16(RSignVStr, "UTF-8");//签名
        log.info("返回SHA-1摘要结果：" + RSignature);

        if (SignatureUtils.verifySignature(baofucerPath, RSignature, RSign)) {
            log.info("验签：Yes----");//验签成功
            if (!RDateArry.containsKey("resp_code")) {
                throw new XkdException("缺少resp_code参数！");
            }
            String trans_id = RDateArry.get("trans_id");
            RepaymentOrderInfo repaymentOrderInfo = null;
            String payStatus = "";
            String failCause = null;
            for (int i = 0; i < 10; i++) {
                repaymentOrderInfo = repaymentOrderInfoMapper.selectOne(new LambdaQueryWrapper<RepaymentOrderInfo>().eq(RepaymentOrderInfo::getOrderNo, trans_id));
                log.info("第{}次尝试获取代扣订单：{}", i + 1, repaymentOrderInfo);
                if (repaymentOrderInfo == null) {
                    Thread.sleep(500);
                } else {
                    break;
                }
            }
            log.info("是否为空---->" + repaymentOrderInfo + trans_id);
            if (repaymentOrderInfo == null)
                throw new XkdException("代扣订单未生成，请稍后再试");
            OutInfo outInfo = outInfoMapper.selectById(repaymentOrderInfo.getOutId());
            String payPass = outInfo.getPayPass();
            if (RDateArry.get("resp_code").toString().equals("S")) {
                payStatus = "2";
                log.info("订单支付成功！[trans_id:" + trans_id + "]");

                //修改订单表状态
                if (!repaymentOrderInfo.getPayStatus().equals("1")) {
                    return;
                }
                String type = repaymentOrderInfo.getType();
                Integer debtId = repaymentOrderInfo.getDebtId();
                Integer repayId = repaymentOrderInfo.getOrderId();
                Debt debt = debtMapper.selectOne(new LambdaQueryWrapper<Debt>().eq(Debt::getId, debtId));
                //计算需要扣除的服务费
                Integer companyId = repaymentOrderInfo.getCompanyId();
                Company company = companyMapper.selectById(companyId);
                //验证公司是否独立核算
                String accountingType = company.getAccountingType();
                Integer payCompanyId = companyId;
                Integer lockCompanyId = null;
                //判断是否是独立扣费
                if (!accountingType.equals(SysConstant.ACCOUNTING_TYPE_ONE)) {
                    Integer blocId = company.getBlocId();
                    company = companyMapper.selectById(blocId);
                    payCompanyId = blocId;
                    lockCompanyId = blocId;
                }
                BigDecimal FWMoney = new BigDecimal(repaymentOrderInfo.getAmount()).multiply(company.getCollectionDeductionServiceFee()).divide(new BigDecimal(100000), 2, RoundingMode.HALF_UP);
                //修改公司回款累计金额 并且 扣除服务费
                String lockName = SysConstant.FEE_LOCK + lockCompanyId;
                RLock lock = redissonClient.getLock(lockName);
                try {
                    log.info("尝试获取锁: {}", lockName);
                    lock.lock();
                    log.info("成功获取锁: {}", lockName);
                    //扣除服务费
                    if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {//通道一
                        companyMapper.updateReturnAmount(repaymentOrderInfo.getAmount(), companyId);
                    } else if (payPass.equals(SysConstant.CHANNEL_TYPE_TWO)) {//通道二
                        companyMapper.updateTowReturnAmount(repaymentOrderInfo.getAmount(), companyId);
                    }
                    companyMapper.updateBalance(FWMoney, payCompanyId);
                } finally {
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                        log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), lockName);
                    }
                }
                //记录服务费明细
                BigDecimal repayAmount = new BigDecimal(repaymentOrderInfo.getAmount()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);//还款金额
                ConsumeDetail consumeDetail = ConsumeDetail.builder()
                        .createTime(new Date())
                        .cost(FWMoney)
                        .amount(repayAmount)
                        .type(SysConstant.COST_TYPE_SEVEN)
                        .payType(SysConstant.COST_REPAY_TYPE_ONE)
                        .rateConfig(company.getCollectionDeductionServiceFee() + "‰")
                        .companyId(companyId)
                        .transactionType(SysConstant.TRANSACTION_TYPE_ONE)
                        .orderNo(trans_id)
                        .build();
                consumeDetailMapper.insert(consumeDetail);
                if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_ONE) || type.equals(SysConstant.DEDUCT_MONEY_TYPE_TOW)) {
                    RepaymentSchedule repaymentSchedule = repaymentScheduleMapper.selectOne(new LambdaQueryWrapper<RepaymentSchedule>().eq(RepaymentSchedule::getId, repaymentOrderInfo.getOrderId()));
                    //查询是否最后一期
                    String remainingRepaymentPeriodsStatus = null;
                    //是否有逾期
                    String isOverdue = null;
                    if (debt.getRemainingRepaymentPeriods() == 1) {//还剩一期 代表还清了
                        remainingRepaymentPeriodsStatus = "yes";
                        //生产贷款结清证明  异步调用
                        eventPublisher.publishEvent(new DebtEvent(debt));
                    }
                    Integer count = repaymentScheduleMapper.selectisOverdueList(repaymentSchedule);
                    if (count > 0) {
                        isOverdue = "yes";
                    }
                    //修改债权金额信息
                    debtMapper.updateRepaymentPeriods(debtId, repaymentSchedule.getPrincipal(), repaymentSchedule.getInterest(), remainingRepaymentPeriodsStatus, isOverdue);
                    //修改还款信息
                    repaymentScheduleMapper.updateStatus(repaymentOrderInfo.getOrderId(), repayAmount);
//                    repaymentOrderInfoMapper.updateStatus(trans_id, "2", null);
                } else if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_FOUR) || type.equals(SysConstant.DEDUCT_MONEY_TYPE_FIVE)) {
                    //修改债权金额信息
                    debtMapper.updateEarlyInfo(repaymentOrderInfo, repayAmount);
                    //修改还款表所有账单状态
                    Integer outId = debt.getOutId();
                    repaymentScheduleMapper.updateStatusByOutId(outId);
                    //生产贷款结清证明  异步调用
                    eventPublisher.publishEvent(new DebtEvent(debt));
                }
                repaymentOrderInfoMapper.updateStatus(trans_id, payStatus, failCause, FWMoney);
                //如果是自动扣款 查询是否有下一期扣款
                if (repaymentOrderInfo.getType().equals(SysConstant.DEDUCT_MONEY_TYPE_ONE) || type.equals(SysConstant.DEDUCT_MONEY_TYPE_TOW)) {
                    // 确保锁的释放在事务提交之后
                    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                        @Override
                        public void afterCommit() {//事务成功后执行
                            try {
                                iAsyncService.getNext(repayId);
                            } catch (Exception e) {
                                throw new XkdException("下一期扣款错误");
                            }
                        }
                    });
                }
            } else {
                payStatus = "3";
                failCause = RDateArry.get("biz_resp_msg").toString();
                repaymentOrderInfoMapper.updateStatus(trans_id, payStatus, failCause, null);
            }
            //如果是第三方系统数据
            if (payPass.equals(SysConstant.CHANNEL_TYPE_TWO)) {
                String incomingType = incomingInfoMapper.selectById(repaymentOrderInfo.getIncomingId()).getIncomingType();
                if (incomingType.equals("2"))
                    iAsyncService.advanceSinglePay(repaymentOrderInfo, payStatus, failCause);
            }
        } else {
            log.info("验签失败：NO");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void baodanSinglePayReturn(HttpServletRequest request) throws Exception {
        request.setCharacterEncoding("utf-8");//utf-8编码接收
        RequestParmUtil.getParamLog(request);//打印接收参数

        Map<String, String> RDateArry = new TreeMap<String, String>();
        Enumeration<String> e = request.getParameterNames();
        while (e.hasMoreElements()) {
            String paraStr = e.nextElement();
            if (paraStr != null) {
                RDateArry.put(paraStr, request.getParameter(paraStr));//接收参数,（使用TreeMap自动排序）
            }
        }

        if (!RDateArry.containsKey("signature")) {
            throw new XkdException("异常缺少验签参数！");
        }
        String RSign = RDateArry.get("signature");
        log.info("返回的签名值：" + RSign);
        RDateArry.remove("signature");//需要删除签名字段
        String RSignVStr = FormatUtil.coverMap2String(RDateArry);
        log.info("返回SHA-1摘要字串：" + RSignVStr);
        String RSignature = SecurityUtil.sha1X16(RSignVStr, "UTF-8");//签名
        log.info("返回SHA-1摘要结果：" + RSignature);

        if (SignatureUtils.verifySignature(baofucerPath, RSignature, RSign)) {
            BigDecimal FWMoney = BigDecimal.ZERO;
            log.info("验签：Yes----");//验签成功
            if (!RDateArry.containsKey("resp_code")) {
                throw new XkdException("缺少resp_code参数！");
            }
            String trans_id = RDateArry.get("trans_id");
            String status = null;
            String failCause = null;
            if (RDateArry.get("resp_code").toString().equals("S")) {
                log.info("订单支付成功！[trans_id:" + trans_id + "]");
                AdvancePaymentOrder advancePaymentOrder = null;
                for (int i = 0; i < 10; i++) {
                    log.info("第{}次尝试获取订单：{}", i + 1, advancePaymentOrder);
                    advancePaymentOrder = advancePaymentOrderMapper.selectOne(new LambdaQueryWrapper<AdvancePaymentOrder>().eq(AdvancePaymentOrder::getOrderNo, trans_id));
                    if (advancePaymentOrder == null) {
                        Thread.sleep(500);
                    } else {
                        break;
                    }
                }
                log.info("是否为空---->" + advancePaymentOrder + trans_id);
                //修改订单表状态
                if (!advancePaymentOrder.getPayStatus().equals("1")) {
                    return;
                }
                //扣除服务费
                Integer companyId = advancePaymentOrder.getCompanyId();
                //是否独立核算
                Company company = companyMapper.selectById(companyId);
                Integer payCompanyId = companyId;
                Integer lockCompanyId = null;
                if (!company.getAccountingType().equals(SysConstant.ACCOUNTING_TYPE_ONE)) {
                    Integer blocId = company.getBlocId();
                    company = companyMapper.selectById(blocId);
                    payCompanyId = blocId;
                    lockCompanyId = blocId;
                }
                FWMoney = new BigDecimal(advancePaymentOrder.getAmount()).multiply(company.getCollectionDeductionServiceFee()).divide(new BigDecimal(100000), 2, RoundingMode.HALF_UP);

                String lockName = SysConstant.FEE_LOCK + lockCompanyId;
                RLock lock = redissonClient.getLock(lockName);
                try {
                    log.info("尝试获取锁: {}", lockName);
                    lock.lock();
                    log.info("成功获取锁: {}", lockName);
                    //修改回款金额
                    OutInfo outInfo = outInfoMapper.selectById(advancePaymentOrder.getOutId());
                    IncomingInfo incomingInfo = incomingInfoMapper.selectById(advancePaymentOrder.getIncomingId());
                    String payPass = outInfo.getPayPass();
                    if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {//通道一
                        companyMapper.updateReturnAmount(advancePaymentOrder.getAmount(), companyId);
                    } else if (payPass.equals(SysConstant.CHANNEL_TYPE_TWO)) {//通道二
                        companyMapper.updateTowReturnAmount(advancePaymentOrder.getAmount(), companyId);
                    }
                    companyMapper.updateBalance(FWMoney, payCompanyId);
                    status = "2";
                    //修改预付款状态
                    outInfoMapper.updateAdvanceStatus(incomingInfo.getIncomingId());
                } finally {
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                        log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), lockName);
                    }
                }
                incomingInfoMapper.updateStatus(SysConstant.BUSINESS_STATUS_ELEVEN, advancePaymentOrder.getIncomingId());

                //记录服务费明细
                BigDecimal repayAmount = new BigDecimal(advancePaymentOrder.getAmount()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);//预付款金额
                ConsumeDetail consumeDetail = ConsumeDetail.builder()
                        .createTime(new Date())
                        .cost(FWMoney)
                        .amount(repayAmount)
                        .type(SysConstant.COST_TYPE_SEVEN)
                        .payType(SysConstant.COST_REPAY_TYPE_ONE)
                        .rateConfig(company.getCollectionDeductionServiceFee() + "‰")
                        .companyId(companyId)
                        .transactionType(SysConstant.TRANSACTION_TYPE_ONE)
                        .orderNo(trans_id)
                        .build();
                consumeDetailMapper.insert(consumeDetail);
            } else {
                failCause = RDateArry.get("biz_resp_msg").toString();
                status = "3";
            }
            advancePaymentOrderMapper.updateStatus(trans_id, status, failCause, FWMoney);
        } else {
            log.info("验签失败：NO");
        }
    }

    @Override
    public void baodanSharenotify(Map<String, String> sharerec) throws Exception {
        log.info("执行保单贷预付款扣款分账回调");
        if (sharerec == null) throw new XkdException("Map is Not");
        if (sharerec.keySet().size() <= 0) throw new XkdException("Parm is Not");
        log.info("保单贷预付款分账结果异步通知入参：" + sharerec);

        Map<String, String> RDateArry = new TreeMap<String, String>();
        RDateArry.putAll(sharerec);
        if (!RDateArry.containsKey("signature")) {
            throw new XkdException("异常缺少验签参数！");
        }

        String RSign = RDateArry.get("signature");
        log.info("返回的签名值：" + RSign);
        RDateArry.remove("signature");//需要删除签名字段
        String RSignVStr = FormatUtil.coverMap2String(RDateArry);
        log.info("返回SHA-1摘要字串：" + RSignVStr);
        String RSignature = SecurityUtil.sha1X16(RSignVStr, "UTF-8");//签名
        log.info("返回SHA-1摘要结果：" + RSignature);

        if (SignatureUtils.verifySignature(baofucerPath, RSignature, RSign)) {
            log.info("验签：Yes");//验签成功
            if (!RDateArry.containsKey("resp_code")) {
                throw new XkdException("缺少resp_code参数！");
            }
            if (RDateArry.get("resp_code").toString().equals("S")) {
                String s = RDateArry.get("trans_id");
                log.info("分账成功[trans_id:{}]", s);
                //修改分账状态
                advancePaymentOrderMapper.updateShareStatus(s);
            } else {
                throw new Exception("反回异常！");//异常不得做为订单状态。
            }
        } else log.info("验签失败：NO");
    }



    @Override
    public void HKtowBaofuwithdrawDeposit(TowWithdrawDepositDto towWithdrawDepositDto) throws Exception {
        log.info("执行回款账户渠道二提现");
        checkBalanceAndFreeze(towWithdrawDepositDto.getCompanyId());
        String amount = towWithdrawDepositDto.getAmount();//元
        long penny = new BigDecimal(amount).multiply(new BigDecimal(100)).longValue();//分
        Integer companyId = towWithdrawDepositDto.getCompanyId();
        String lockName = towBaofuwithdrawLock + companyId;
        RLock lock = redissonClient.getLock(lockName);
        try {
            log.info("尝试获取锁: {}", lockName);
            boolean b = lock.tryLock(10, TimeUnit.MILLISECONDS);
            if (!b) {
                log.info("获取锁失败: {}", lockName);
                throw new Exception("服务器繁忙！");
            }
            log.info("成功获取锁: {}", lockName);
            Company company = companyMapper.selectById(companyId);
            long i = 0;
            if (towWithdrawDepositDto.getType().equals("1")) {//放款账户提现
                i = company.getTowBaofuBalance() - company.getTowOutBalanceFreeze();//放款账户余额减去冻结金额=可用金额
            } else {//回款账户提现
                i = company.getTowReturnBalance() - company.getTowReturnBalanceFreeze();//回款账户余额减去冻结金额=可用金额
            }
            if (i < penny)
                throw new XkdException("该资方渠道二账户可用余额不足");
            String order = OrderNumUtil.getOutAmountOrder();
            //受理成功 入库
            ChannelPayTow channelPayTow = new ChannelPayTow();
            channelPayTow.setOrderNo(order);
            channelPayTow.setPayType("2");
            channelPayTow.setAmount(penny);
            channelPayTow.setAccountType(towWithdrawDepositDto.getType());
            channelPayTow.setPayStatus("2");
            channelPayTow.setCompanyId(company.getId());
            channelPayTow.setCreateTime(new Date());
            channelPayTow.setTradeTime(new Date());
            channelPayTow.setToAccName(towWithdrawDepositDto.getToAccNameBaofu());
            channelPayTow.setToAccNo(towWithdrawDepositDto.getToAccNoBaofu());
            channelPayTow.setToBankName(towWithdrawDepositDto.getToBankNameBoafu());
            channelPayTow.setVoucher(towWithdrawDepositDto.getVoucher());
            if (org.apache.commons.lang3.StringUtils.isNotBlank(towWithdrawDepositDto.getVoucher())) {
                channelPayTow.setUploadDate(new Date());
            }
            channelPayTowMapper.insert(channelPayTow);
            iCompanyService.deductingBalanceMain(null, null, null, null,
                    SysConstant.BUSINESS_FEE_EIGHT, companyId,
                    new BigDecimal(amount).multiply(BigDecimal.valueOf(100))+"", channelPayTow.getOrderNo());
            if (towWithdrawDepositDto.getType().equals("1")) {//放款账户提现
                companyMapper.updateTowBaofuBalance(channelPayTow.getCompanyId(), penny);
            } else {//回款账户提现
                companyMapper.updateTowReturnBalance(channelPayTow.getCompanyId(), penny);
            }
            log.info("渠道二提现执行完毕");
        } catch (Exception e) {
            log.error("渠道二提现，请求参数: {}, 锁键: {}, 异常信息: {}", towWithdrawDepositDto, lockName, e.getMessage(), e);
            throw e;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("渠道二提现任务释放锁，锁键: {}", lockName);
            }
        }
    }

    @Override
    public Boolean whetherReadySign(ReadySignDto readySignDto) {
        CardProtocolInfo cardProtocolInfo = cardProtocolInfoMapper.selectOne(new LambdaQueryWrapper<CardProtocolInfo>().
                eq(CardProtocolInfo::getRepayAccount, readySignDto.getRepayAccount()).
                eq(CardProtocolInfo::getRepayName, readySignDto.getRepayName()));
        if (cardProtocolInfo != null) {//已绑卡
            Credit credit = new Credit();
            credit.setCardStatus("1");
            credit.setRepayAccount(cardProtocolInfo.getRepayAccount());
            credit.setRepayName(cardProtocolInfo.getRepayName());
            credit.setRepayBank(cardProtocolInfo.getRepayBank());
            LambdaQueryWrapper<Credit> lqw = new LambdaQueryWrapper<>();
            lqw.eq(Credit::getIncomingId, readySignDto.getIncomingId());
            creditMapper.updateCard(credit, readySignDto.getIncomingId());
            return true;
        } else {//未绑卡
            return false;
        }
    }

    @Override
    public void h5SinglePay(AdvancePaymentDto advancePaymentDto) throws Exception {
        Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, advancePaymentDto.getIncomingId()));
        String TransId = OrderNumUtil.getRepayOrder();
        long multiply = credit.getAdvanceAmount().multiply(new BigDecimal(100)).longValue();

        String lockKey = ADVANCE_LOCK_KEY + advancePaymentDto.getIncomingId();
        RLock lock = redissonClient.getLock(lockKey);

        try {
            log.info("尝试获取锁: {}", lockKey);
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                log.info("成功获取锁: {}", lockKey);
                log.info("执行保单贷预付款扣款");
                Integer i = advancePaymentOrderMapper.selectCount(new LambdaQueryWrapper<AdvancePaymentOrder>().
                        eq(AdvancePaymentOrder::getIncomingId, advancePaymentDto.getIncomingId()).
                        in(AdvancePaymentOrder::getPayStatus, "1", "2"));
                if (i > 0)
                    throw new XkdException("请勿重复申请预付款");

                AdvancePaymentOrder advancePaymentOrder = new AdvancePaymentOrder();
                advancePaymentOrder.setOrderNo(TransId);
                advancePaymentOrder.setAmount(multiply);
                advancePaymentOrder.setType(advancePaymentDto.getType());
                advancePaymentOrder.setCompanyId(credit.getCompanyId());
                advancePaymentOrder.setCreateTime(new Date());
                advancePaymentOrder.setRepayName(credit.getRepayName());
                advancePaymentOrder.setRepayAccount(credit.getRepayAccount());
                advancePaymentOrder.setRepayBank(credit.getRepayBank());
                advancePaymentOrder.setIncomingId(advancePaymentDto.getIncomingId());
                advancePaymentOrder.setOutId(advancePaymentDto.getOutId());

                CompanyAccount companyAccount = companyAccountMapper.selectById(credit.getCompanyId());
                String send_time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//报文发送日期时间 //send_time="2038-12-23 10:00:00";
                CardProtocolInfo cardProtocolInfo = cardProtocolInfoMapper.selectOne(new LambdaQueryWrapper<CardProtocolInfo>().eq(CardProtocolInfo::getRepayAccount, credit.getRepayAccount()));
                if (cardProtocolInfo == null)
                    throw new XkdException("已解绑银行卡");
                String ProtocolNo = cardProtocolInfo.getProtocolNo();//签约协议号（确认绑卡返回）
                OutInfo outInfo = outInfoMapper.selectById(advancePaymentDto.getOutId());
                String payPass = outInfo.getPayPass();
                String MemberId = null;
                String TerminalId = null;
                String txn_type = null;
                if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {
                    MemberId = in_member_id;//商户号
                    TerminalId = in_terminal_id;
                    txn_type = "18";//分账
                } else if (payPass.equals(SysConstant.CHANNEL_TYPE_TWO)) {
                    MemberId = tow_in_member_id;//商户号
                    TerminalId = tow_in_terminal_id;
                    txn_type = "08";//不分账
                }

                String pfxpath = baopriPath;//商户私钥
                String cerpath = baofucerPath;//宝付公钥
                String pfxpwd = pfxPathPassword;//私钥密码


                log.info("订单号：" + TransId);

                String AesKey = FormatUtil.CreateAeskey();//商户自定义(可随机生成  AES key长度为=16位)
                String dgtl_envlp = "01|" + AesKey;//+AesKey;//使用接收方的公钥加密后的对称密钥，并做Base64转码，明文01|对称密钥，01代表AES[密码商户自定义]
                log.info("密码dgtl_envlp：" + dgtl_envlp);
                dgtl_envlp = RsaCodingUtil.encryptByPubCerFile(SecurityUtil.Base64Encode(dgtl_envlp), cerpath);//公钥加密
                log.info("RSA公钥加密结果" + dgtl_envlp);


                ProtocolNo = SecurityUtil.AesEncrypt(SecurityUtil.Base64Encode(ProtocolNo), AesKey);//先BASE64后进行AES加密
                log.info("签约协议号AES结果:" + ProtocolNo);

                String CardInfo = "";//信用卡：信用卡有效期|安全码,借记卡：传空

                //暂不支持信用卡
                CardInfo = SecurityUtil.AesEncrypt(SecurityUtil.Base64Encode(CardInfo), AesKey);//先BASE64后进行AES加密


                Map<String, String> DateArry = new TreeMap<String, String>();
                DateArry.put("send_time", send_time);
                DateArry.put("msg_id", "FMXC" + FormatUtil.CreateAeskey(28));//报文流水号
                DateArry.put("version", "4.0.0.0");
                DateArry.put("terminal_id", TerminalId);
                DateArry.put("txn_type", txn_type);//交易类型(参看：文档中《交易类型枚举》)
                DateArry.put("member_id", MemberId);
                DateArry.put("trans_id", TransId);
                DateArry.put("dgtl_envlp", dgtl_envlp);
                DateArry.put("user_id", "");//用户在商户平台唯一ID (和绑卡时要一致)
                DateArry.put("protocol_no", ProtocolNo);//签约协议号（密文）
                DateArry.put("txn_amt", String.valueOf(multiply));//交易金额 [单位：分  例：1元则提交100]，此处注意数据类型的转转，建议使用BigDecimal类弄进行转换为字串
                DateArry.put("card_info", CardInfo);//卡信息
                if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {
                    DateArry.put("share_notify_url", baodansharenotify);//分账异步通知
                    //100%分账
                    String share_info = companyAccount.getPrestoreMemberId() + "," + multiply + ";";
                    DateArry.put("share_info", share_info);//分账
                }
//		DateArry.put("fee_member_id", "");//手续费商户

                Map<String, String> RiskItem = new HashMap<String, String>();
                /**--------风控基础参数-------------**/
                /**
                 * 说明风控参数必须，按商户开通行业、真实交易信息传，不可传固定值。
                 */
                RiskItem.put("goodsCategory", "02");//商品类目 详见附录《商品类目》 平台为互消金融
                RiskItem.put("chPayIp", "127.0.0.1");//持卡人支付IP
                RiskItem.put("deviceOrderNo", "");//加载设备指纹中的订单号


                DateArry.put("risk_item", net.sf.json.JSONObject.fromObject(RiskItem).toString());//放入风控参数
                DateArry.put("return_url", baodansinglePayReturn);//最多填写三个地址,不同地址用间使用‘|’分隔 //异步通知接收地址

                String SignVStr = FormatUtil.coverMap2String(DateArry);
                log.info("SHA-1摘要字串：" + SignVStr);
                String signature = SecurityUtil.sha1X16(SignVStr, "UTF-8");//签名
                log.info("SHA-1摘要结果：" + signature);
                String Sign = SignatureUtils.encryptByRSA(signature, pfxpath, pfxpwd);
                log.info("RSA签名结果：" + Sign);
                DateArry.put("signature", Sign);//签名域

                String PostString = HttpUtil.RequestForm(xieyiurl, DateArry);
                log.info("请求返回:" + PostString);

                Map<String, String> ReturnData = FormatUtil.getParm(PostString);

                if (!ReturnData.containsKey("signature")) {
                    throw new XkdException("缺少验签参数！");
                }
                String RSign = ReturnData.get("signature");
                log.info("返回的验签值：" + RSign);
                ReturnData.remove("signature");//需要删除签名字段
                String RSignVStr = FormatUtil.coverMap2String(ReturnData);
                log.info("返回SHA-1摘要字串：" + RSignVStr);
                String RSignature = SecurityUtil.sha1X16(RSignVStr, "UTF-8");//签名
                log.info("返回SHA-1摘要结果：" + RSignature);

                if (SignatureUtils.verifySignature(cerpath, RSignature, RSign)) {
                    log.info("验签：Yes");//验签成功

                    if (!ReturnData.containsKey("resp_code")) {
                        throw new XkdException("缺少resp_code参数！");
                    }
                    try {
                        if (ReturnData.get("resp_code").toString().equals("S")) {
                            log.info("申请支付成功！");
                        } else if (ReturnData.get("resp_code").toString().equals("I")) {
                            log.info("交易处理中！");
                        } else if (ReturnData.get("resp_code").toString().equals("F")) {
                            advancePaymentOrder.setPayStatus("3");
                            advancePaymentOrder.setFailCause(ReturnData.get("biz_resp_msg").toString());
                            throw new XkdException("交易失败！" + ReturnData.get("biz_resp_msg").toString());//异常不得做为订单状态。
                        } else {
                            throw new XkdException("反回异常！");//异常不得做为订单状态。
                        }
                    } finally {
                        advancePaymentOrderMapper.insert(advancePaymentOrder);
                    }

                } else {
                    log.info("验签：NO");
                }
            } else {
                throw new XkdException("服务器繁忙，请勿重复提交");
            }
        } catch (Exception e) {
            log.error("执行保单贷预付款扣款，锁键: {}, 锁异常: {}", lockKey, e.getMessage(), e);
            throw e;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("手动代扣任务释放锁，锁键: {}", lockKey);
            }
        }


    }

    @Override
    public String VDownload(VDownloadDto vDownloadDto) throws Exception {
        OutInfo outInfo = outInfoMapper.selectById(vDownloadDto.getOutId());
        if (outInfo == null)
            throw new XkdException("无此订单");
        String transferDate = "";
        String memberId = null;
        String terminalId = null;
        if (outInfo.getPayPass().equals(SysConstant.CHANNEL_TYPE_ONE)) {
            OutOrderInfo outOrderInfo = outOrderInfoMapper.selectOne(new LambdaQueryWrapper<OutOrderInfo>().eq(OutOrderInfo::getOrderNo, vDownloadDto.getOrderNo()));
            transferDate = DateUtil.format(outOrderInfo.getTradeTime(), "yyyy-MM-dd");
            memberId = out_member_id;//商户号
            terminalId = out_terminal_id;//终端号
        } else if (outInfo.getPayPass().equals(SysConstant.CHANNEL_TYPE_TWO)) {
            ChannelPayTow channelPayTow = channelPayTowMapper.selectOne(new LambdaQueryWrapper<ChannelPayTow>().eq(ChannelPayTow::getOrderNo, vDownloadDto.getOrderNo()));
            transferDate = DateUtil.format(channelPayTow.getTradeTime(), "yyyy-MM-dd");
            memberId = tow_out_member_id;//商户号
            terminalId = tow_out_terminal_id;
        }
        String Transid = vDownloadDto.getOrderNo();//商户订单号
        String pfxpath = baopriPath;//商户私钥
        String cerpath = baofucerPath;//宝付公钥
        String pfxpwd = pfxPathPassword;//私钥密码


        Map<String, String> HeaderPost = new HashMap<String, String>();
        HeaderPost.put("memberId", memberId);//商户号
        HeaderPost.put("terminalId", terminalId);//终端号
        HeaderPost.put("serviceTp", "T-1001-003-03");//服务编号
        HeaderPost.put("verifyType", "1");//加密方式

        Map<String, Object> BodyData = new TreeMap<String, Object>();
        BodyData.put("memberTransId", Transid);//商户订单号
//        BodyData.put("noticeUrl", "http://10.1.60.128:8080/ReturnUrl.action");
        BodyData.put("fileType", "2");//文件类型1 PNG 2PDF
        BodyData.put("orderType", vDownloadDto.getOrderType());//订单类型 默认代付交易 0代付 1票据  3代收协议支付 4 普通代收
        BodyData.put("transferDate", transferDate);//交易日期yyyy-MM-dd

        Map<String, Object> contentData = new TreeMap<String, Object>();
        contentData.put("header", HeaderPost);
        contentData.put("body", BodyData);

        net.sf.json.JSONObject jsonObjectFromMap = net.sf.json.JSONObject.fromObject(contentData);
        String Jsonstr = jsonObjectFromMap.toString();
        log.info("序列化：" + Jsonstr);

        String base64str = SecurityUtil.Base64Encode(Jsonstr);
        String data_content = RsaCodingUtil.encryptByPriPfxFile(base64str, pfxpath, pfxpwd);
        Map<String, String> PostParm = new HashMap<String, String>();
        PostParm.put("memberId", memberId);//商户号
        PostParm.put("terminalId", terminalId);//终端号
        PostParm.put("veryfyString", "");
        PostParm.put("verifyType", "1");//加密方式
        PostParm.put("content", data_content);

//        String url = "https://vgw.baofoo.com/union-gw/api/";
        String url = "https://public.baofu.com/union-gw/api/";
        String PostString = HttpUtil.RequestForm(url + HeaderPost.get("serviceTp") + "/transReq.do", PostParm);

        log.info("请求返回：" + PostString);

        PostString = RsaCodingUtil.decryptByPubCerFile(PostString, cerpath);
        if (PostString.isEmpty()) {//判断解密是否正确。如果为空则宝付公钥不正确
            log.info("=====检查解密公钥是否正确！");
        }
        PostString = SecurityUtil.Base64Decode(PostString);
        log.info("=====返回数据解密结果:" + PostString);

        TreeMap<String, String> ArrayData = JXMConvertUtil.JsonConvertHashMap(PostString);//将JSON转化为Map对象。
        log.info("转换为MAP对象：" + ArrayData);
        if (!ArrayData.containsKey("sysRespCode")) {
            throw new Exception("sysRespCode Not Found Key [state]!");
        }
        log.info("sysRespCode:" + ArrayData.get("sysRespCode").toString());

        if (ArrayData.get("sysRespCode").toString().equals("S_0000")) {
            if (!ArrayData.containsKey("state")) {
                throw new Exception("sysRespCode Not Found Key [state]!");
            }

            if (ArrayData.get("state").toString().equals("0000")) {
                log.info("获取成功！");
                String s = ArrayData.get("urlDownload");
                if (StringUtils.isBlank(s)) {
                    log.info("申请下载地址成功，请再次获取！");
                    return "";
                }
                return ArrayData.get("urlDownload").toString();
            }
        }
        return "";
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public String offlinePay(OfflinePayDto offlinePayDto) throws Exception {
        Integer amount = null;
        String type = offlinePayDto.getType();
        Debt debt = debtMapper.selectOne(new LambdaQueryWrapper<Debt>().eq(Debt::getId, offlinePayDto.getDebtId()));
        Integer outId = debt.getOutId();
        if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_THREE)) {//线下还款本期
            //判断是否已经是线下状态
            RepaymentSchedule repaymentSchedule = repaymentScheduleMapper.selectOne(new LambdaQueryWrapper<RepaymentSchedule>().eq(RepaymentSchedule::getId, offlinePayDto.getRepayId()));
            if (repaymentSchedule.getRepaymentType().equals("2"))
                throw new XkdException("已申请线下打款");
            if (repaymentSchedule.getStatus().equals(SysConstant.REPAYMENT_STATUS_TOW))
                throw new XkdException("本期已还");
            Integer amount1 = repaymentSchedule.getAmount().multiply(new BigDecimal(100)).intValue();
            Integer amount2 = debt.getOverdueInterest().add(debt.getDefaultInterestTodo()).multiply(new BigDecimal(100)).intValue();
            amount = amount1 + amount2;
        } else if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_SIX)) {//线下还款 提前还款
            if (debt.getStatus().equals(2))
                throw new XkdException("已结清");
            if (debt.getIsOfflineRepay().equals("1"))
                throw new XkdException("已申请线下打款");
            amount = debt.getEarlyAmount().multiply(new BigDecimal(100)).intValue();
        }

        CompanyAccount companyAccount = companyAccountMapper.selectById(offlinePayDto.getCompanyId());
        String payee_acct_code = "";//收款方账户号
        String payer_acct_code = offlinePayDto.getPayerAcctCode();//付款方账户号
        String payer_user_name = offlinePayDto.getPayerUserName();//付款方开户名
        String bank_serial_no = "";//银行流水号
        String order_money = amount.toString();//金额单位为（分）如：1234 为12.34元

        String share_info = companyAccount.getPrestoreMemberId() + "," + order_money + ";";//分账信息（商户1,金额1;商户2,金额2..）
        log.info("分账信息" + share_info);
        OutInfo outInfo = outInfoMapper.selectById(outId);
        String payPass = outInfo.getPayPass();
        String member_id = null;
        String terminal_id = null;
        if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {
            member_id = in_member_id;//商户号
            terminal_id = in_terminal_id;//终端号
            payee_acct_code = loans_in_account_offline;//收款方账户号
        } else if (payPass.equals(SysConstant.CHANNEL_TYPE_TWO)) {
            member_id = tow_in_member_id;//商户号
            terminal_id = tow_in_terminal_id;
            payee_acct_code = tow_loans_in_account_offline;//收款方账户号
        }
        String pfxpath = baopriPath;//商户私钥
        String cerpath = baofucerPath;//宝付公钥
        String pfxpwd = pfxPathPassword;//私钥密码
        String Trans_id = "";
        Trans_id = OrderNumUtil.getRepayOrder();
//        String Trans_id ="FMXC"+FormatUtil.CreateAeskey(26);
        log.info("商户订单号:" + Trans_id);
        String txn_sub_type = "88";//交易子类
        Map<String, String> HeadPostParam = new HashMap<String, String>();
        HeadPostParam.put("version", "4.0.0.0");
        HeadPostParam.put("member_id", member_id);
        HeadPostParam.put("terminal_id", terminal_id);
        HeadPostParam.put("txn_type", "0431");
        HeadPostParam.put("data_type", "json");
        HeadPostParam.put("txn_sub_type", txn_sub_type); //交易子类


        Map<String, String> DataContent = new HashMap<String, String>();
        DataContent.put("member_id", member_id);
        DataContent.put("terminal_id", terminal_id);
        DataContent.put("txn_sub_type", txn_sub_type);
        DataContent.put("trans_id", Trans_id);//商户订单号(注：唯一不能重复)
        DataContent.put("trans_serial_no", "FMXC" + FormatUtil.CreateAeskey(26));//每次请求都不可重复
        DataContent.put("biz_type", "0431");
        DataContent.put("order_money", order_money);
        DataContent.put("payee_acct_code", payee_acct_code);//收款方账户号
        DataContent.put("payer_acct_code", payer_acct_code);//付款方账户号
        DataContent.put("payer_user_name", payer_user_name);//付款方开户名
        DataContent.put("bank_serial_no", bank_serial_no);
        DataContent.put("call_back_url", offlinePayNotify);//交易异步通知地址
        if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {
            DataContent.put("share_info", share_info);//分账信息
            DataContent.put("share_notify_url", offlineSplitNotify);//分账通知地址
        }
        DataContent.put("additional_info", "");//附加字段
        DataContent.put("req_reserved", "");

        String XmlOrJson = JSON.toJSONString(DataContent);
        log.info("序列化：" + XmlOrJson);
        String data_content = RsaCodingUtil.encryptByPriPfxFile(SecurityUtil.Base64Encode(XmlOrJson), pfxpath, pfxpwd);//公钥加密
        HeadPostParam.put("data_content", data_content);
        log.info("请求参数：" + HeadPostParam);
        //插入订单
        RepaymentOrderInfo repaymentOrderInfo = new RepaymentOrderInfo();
        repaymentOrderInfo.setType(type);
        repaymentOrderInfo.setOrderId(offlinePayDto.getRepayId());
        repaymentOrderInfo.setDebtId(offlinePayDto.getDebtId());
        repaymentOrderInfo.setOrderNo(Trans_id);
        repaymentOrderInfo.setAmount(amount);
        repaymentOrderInfo.setCompanyId(offlinePayDto.getCompanyId());
        repaymentOrderInfo.setRepayAccount(offlinePayDto.getPayerAcctCode());
        repaymentOrderInfo.setRepayName(offlinePayDto.getPayerUserName());
        repaymentOrderInfo.setRepayBank(offlinePayDto.getPayerBank());
        repaymentOrderInfo.setIncomingId(offlinePayDto.getIncomingId());
        repaymentOrderInfo.setOutId(outId);
        repaymentOrderInfoMapper.insert(repaymentOrderInfo);

        if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_THREE)) {//线下还款本期
            repaymentScheduleMapper.updateRepaymentType(offlinePayDto.getRepayId(), "2");
        } else if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_SIX)) {//线下还款 提前还款
            debtMapper.updateRepaymentType(offlinePayDto.getDebtId(), "1");
        }

        String PostString = HttpUtil.RequestForm(zhuanzhang, HeadPostParam);
        log.info("请求返回:" + PostString);

        PostString = RsaCodingUtil.decryptByPubCerFile(PostString, cerpath);//解密

        if (PostString.isEmpty()) throw new XkdException("解密为空");

        PostString = SecurityUtil.Base64Decode(PostString);
        log.info("=====返回查询数据解密结果:" + PostString);
        com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(PostString);
        if (!jsonObject.getString("resp_code").equals("0000")) {
            throw new XkdException(jsonObject.getString("resp_msg"));
        }
        return Trans_id;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void offlinePayNotify(Map<String, Object> fpnre) throws IOException, InterruptedException {
        if (fpnre == null) throw new XkdException("Parms is Null");
        if (fpnre.keySet().size() <= 0) throw new XkdException("No Parms");
        log.info("访问入参：" + fpnre);
        String resultJson = JSON.toJSONString(fpnre);
        ReciveEntity result = JSON.parseObject(resultJson, ReciveEntity.class);
        log.info("转对象：" + result);
        if (result == null) throw new XkdException("对象转换异常");
        if (result.getData_content() == null) throw new XkdException("数据域为空");
        if (result.getData_content().equals("null") || StringUtils.isBlank(result.getData_content()))
            throw new XkdException("数据域为空");
        String ReslutString = RsaCodingUtil.decryptByPubCerFile(result.getData_content(), baofucerPath);
        log.info("解密RSA：" + ReslutString);
        //第二步BASE64解密
        ReslutString = SecurityUtil.Base64Decode(ReslutString);
        log.info("" + ReslutString);
        ///转账支付异步通知订单金额单位为元
        com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(ReslutString);
        log.info("结果" + jsonObject);
        String order = jsonObject.getString("memberTransId");//还款商户订单
        log.info("查询状态---" + order);
        RepaymentOrderInfo repaymentOrderInfo = null;
        for (int i = 0; i < 10; i++) {
            repaymentOrderInfo = repaymentOrderInfoMapper.selectOne(new LambdaQueryWrapper<RepaymentOrderInfo>().eq(RepaymentOrderInfo::getOrderNo, order));
            if (repaymentOrderInfo == null) {
                Thread.sleep(500);
            } else {
                break;
            }
        }
        if (!repaymentOrderInfo.getPayStatus().equals("1"))//状态必须是申请中 避免重复回调
            return;
        Integer id = repaymentOrderInfo.getOrderId();
        Integer debtId = repaymentOrderInfo.getDebtId();
        String type = repaymentOrderInfo.getType();
        Debt debt = debtMapper.selectOne(new LambdaQueryWrapper<Debt>().eq(Debt::getId, debtId));
        String payPass = outInfoMapper.selectById(debt.getOutId()).getPayPass();
        String payStatus = null;
        String failCause = null;
        if (jsonObject.getString("resp_code").equals("0000")) {//成功
            //修改公司回款累计金额
            //计算需要扣除的服务费
            Integer companyId = repaymentOrderInfo.getCompanyId();
            Company company = companyMapper.selectById(companyId);
            //验证公司是否独立核算
            String accountingType = company.getAccountingType();
            Integer payCompanyId = companyId;
            Integer lockCompanyId = null;
            if (!accountingType.equals(SysConstant.ACCOUNTING_TYPE_ONE)) {
                Integer blocId = company.getBlocId();
                company = companyMapper.selectById(blocId);
                payCompanyId = blocId;
                lockCompanyId = blocId;
            }
            BigDecimal FWMoney = new BigDecimal(repaymentOrderInfo.getAmount()).multiply(company.getCollectionTransfeServiceFee()).divide(new BigDecimal(100000), 2, RoundingMode.HALF_UP);

            String lockName = SysConstant.FEE_LOCK + lockCompanyId;
            RLock lock = redissonClient.getLock(lockName);
            try {
                log.info("尝试获取锁: {}", lockName);
                lock.lock();
                log.info("成功获取锁: {}", lockName);
                //扣除服务费锁
                if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {//通道一
                    companyMapper.updateReturnAmount(repaymentOrderInfo.getAmount(), companyId);
                } else if (payPass.equals(SysConstant.CHANNEL_TYPE_TWO)) {//通道二
                    companyMapper.updateTowReturnAmount(repaymentOrderInfo.getAmount(), companyId);
                }
                companyMapper.updateBalance(FWMoney, payCompanyId);
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                    log.info("当前线程: {} 释放锁: {}", Thread.currentThread().getName(), lockName);
                }
            }
            //记录服务费明细
            BigDecimal repayAmount = new BigDecimal(repaymentOrderInfo.getAmount()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
            ConsumeDetail consumeDetail = ConsumeDetail.builder()
                    .createTime(new Date())
                    .cost(FWMoney)
                    .amount(new BigDecimal(repaymentOrderInfo.getAmount()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP))
                    .type(SysConstant.COST_TYPE_SEVEN)
                    .payType(SysConstant.COST_REPAY_TYPE_ONE)
                    .rateConfig(company.getCollectionTransfeServiceFee() + "‰")
                    .companyId(companyId)
                    .transactionType(SysConstant.TRANSACTION_TYPE_ONE)
                    .orderNo(order)
                    .build();
            consumeDetailMapper.insert(consumeDetail);
            if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_THREE)) {//线下还款本期
                RepaymentSchedule repaymentSchedule = repaymentScheduleMapper.selectOne(new LambdaQueryWrapper<RepaymentSchedule>().eq(RepaymentSchedule::getId, id));
                //查询是否最后一期
                String remainingRepaymentPeriodsStatus = null;
                //是否还有逾期
                String isOverdue = null;
                if (debt.getRemainingRepaymentPeriods() == 1) {//还剩一期
                    remainingRepaymentPeriodsStatus = "yes";
                    //生产贷款结清证明  异步调用
                    eventPublisher.publishEvent(new DebtEvent(debt));
                }
                Integer count = repaymentScheduleMapper.selectisOverdueList(repaymentSchedule);
                if (count > 0) {
                    isOverdue = "yes";
                }
                //修改债权金额信息
                debtMapper.updateRepaymentPeriods(debt.getId(), repaymentSchedule.getPrincipal(), repaymentSchedule.getInterest(), remainingRepaymentPeriodsStatus, isOverdue);
                //修改本订单还款状态 实际还款金额
                repaymentScheduleMapper.updateStatus(id, repayAmount);
            } else if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_SIX)) {//线下还款 提前还款
                debtMapper.updateEarlyInfo(repaymentOrderInfo, repayAmount);
                //修改还款表所有账单状态
                Integer outId = debt.getOutId();
                repaymentScheduleMapper.updateStatusByOutId(outId);
                //生产贷款结清证明  异步调用
                eventPublisher.publishEvent(new DebtEvent(debt));
            }
            payStatus = "2";
            repaymentOrderInfoMapper.updateStatus(order, payStatus, null, FWMoney);
        } else {//失败
            if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_THREE)) {//线下还款本期
                repaymentScheduleMapper.updateFailCause(id);
            } else if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_SIX)) {//线下还款 提前还款
                debtMapper.updateFailCause(debtId);
            }
            payStatus = "3";
            failCause = jsonObject.getString("resp_msg");
            repaymentOrderInfoMapper.updateStatus(order, payStatus, failCause, null);
        }
        if (payPass.equals(SysConstant.CHANNEL_TYPE_TWO)) {
            String incomingType = incomingInfoMapper.selectById(repaymentOrderInfo.getIncomingId()).getIncomingType();
            if (incomingType.equals("2")) {
                iAsyncService.advanceSinglePay(repaymentOrderInfo, payStatus, failCause);
            }
        }

    }


    @Override
    public void offlineSplitNotify(Map<String, Object> fpnre) throws IOException {
        if (fpnre == null)
            throw new XkdException("Parms is Null");
        if (fpnre.keySet().size() <= 0) throw new XkdException("No Parms");
        log.info("访问入参：" + fpnre);
        String resultJson = JSON.toJSONString(fpnre);
        ReciveEntity result = JSON.parseObject(resultJson, ReciveEntity.class);
        log.info("转对象：" + result);
        if (result == null) throw new XkdException("对象转换异常");
        if (result.getData_content() == null) throw new XkdException("数据域为空");
        if (result.getData_content().equals("null") || StringUtils.isBlank(result.getData_content()))
            throw new XkdException("数据域为空");
        String ReslutString = RsaCodingUtil.decryptByPubCerFile(result.getData_content(), baofucerPath);
        log.info("解密RSA：" + ReslutString);
        //第二步BASE64解密
        ReslutString = SecurityUtil.Base64Decode(ReslutString);
        log.info("" + ReslutString);
        ///转账支付异步通知订单金额单位为元
        com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(ReslutString);
        log.info("结果" + jsonObject);
        String order = jsonObject.getString("trans_id");//还款商户订单
        log.info("订单号{}", order);
        if (jsonObject.getString("resp_code").equals("BF00338")) {//分账成功
            //todo分账入库
            repaymentOrderInfoMapper.updateShareStatus(order);
        } else {//失败
            log.info("订单号{}，分账失败", order);
        }
    }

    @Override
    public void ShareNotify(Map<String, String> sharerec) throws Exception {
        log.info("执行扣款分账回调");
        if (sharerec == null) throw new XkdException("Map is Not");
        if (sharerec.keySet().size() <= 0) throw new XkdException("Parm is Not");
        log.info("分账结果异步通知入参：" + sharerec);

        Map<String, String> RDateArry = new TreeMap<String, String>();
        RDateArry.putAll(sharerec);
        if (!RDateArry.containsKey("signature")) {
            throw new XkdException("异常缺少验签参数！");
        }

        String RSign = RDateArry.get("signature");
        log.info("返回的签名值：" + RSign);
        RDateArry.remove("signature");//需要删除签名字段
        String RSignVStr = FormatUtil.coverMap2String(RDateArry);
        log.info("返回SHA-1摘要字串：" + RSignVStr);
        String RSignature = SecurityUtil.sha1X16(RSignVStr, "UTF-8");//签名
        log.info("返回SHA-1摘要结果：" + RSignature);

        if (SignatureUtils.verifySignature(baofucerPath, RSignature, RSign)) {
            log.info("验签：Yes");//验签成功
            if (!RDateArry.containsKey("resp_code")) {
                throw new XkdException("缺少resp_code参数！");
            }
            if (RDateArry.get("resp_code").toString().equals("S")) {
                String s = RDateArry.get("trans_id");
                log.info("分账成功[trans_id:{}]", s);
                //修改分账状态
                repaymentOrderInfoMapper.updateShareStatus(s);
            } else {
                throw new Exception("反回异常！");//异常不得做为订单状态。
            }
        } else log.info("验签失败：NO");

    }

    @Override
    public BalanceVo baofuBalance(String payPass) throws Exception {
        String member_id = null;//商户号
        String terminal_id = null;//终端号
        if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {
            member_id = out_member_id;
            terminal_id = out_terminal_id;
        } else if (payPass.equals(SysConstant.CHANNEL_TYPE_TWO)) {
            member_id = tow_out_member_id;
            terminal_id = tow_out_terminal_id;
        }

        String pfxpath = baopriPath;//商户私钥
        String cerpath = baofucerPath;//宝付公钥
        String pfxpwd = pfxPathPassword;//私钥密码


        /* Header 参数*/
        Map<String, String> HeaderPost = new HashMap<String, String>();
        HeaderPost.put("memberId", member_id);//宝付提供给商户的唯一编号
        HeaderPost.put("terminalId", terminal_id);
        HeaderPost.put("serviceTp", "T-1001-006-03");//信用卡还款下单接口
        HeaderPost.put("verifyType", "1");//加密方式目前只有1种，请填：1

        Map<String, Object> BodyData = new TreeMap<String, Object>();
        BodyData.put("version", "4.0.0");//版本号
        BodyData.put("accountType", "BASE_ACCOUNT");//BASE_ACCOUNT:基本户  FREEZE_ACCOUNT:冻结户

        Map<String, Object> contentData = new TreeMap<String, Object>();
        contentData.put("header", HeaderPost);
        contentData.put("body", BodyData);

        String Jsonstr = JSON.toJSONString(contentData);
//        JSONObject jsonObjectFromMap = JSONObject.fromObject(contentData);
//        String Jsonstr = jsonObjectFromMap.toString();
        log.info("序列化：" + Jsonstr);

        String base64str = SecurityUtil.Base64Encode(Jsonstr);
        String data_content = RsaCodingUtil.encryptByPriPfxFile(base64str, pfxpath, pfxpwd);
        HeaderPost.put("content", data_content);

//        String Urlstring = "https://vgw.baofoo.com/union-gw/api/";
        String Urlstring = "https://public.baofu.com/union-gw/api/";
        String PostString = HttpUtil.RequestForm(Urlstring + HeaderPost.get("serviceTp") + "/transReq.do", HeaderPost);
        log.info("请求返回：" + PostString);


        PostString = RsaCodingUtil.decryptByPubCerFile(PostString, cerpath);
        if (PostString.isEmpty()) {//判断解密是否正确。如果为空则宝付公钥不正确
            log.info("=====检查解密公钥是否正确！");
            throw new XkdException("Decryption is incorrect!");
        }
        PostString = SecurityUtil.Base64Decode(PostString);
        log.info("=====返回查询数据解密结果:" + PostString);

        com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(PostString);
        com.alibaba.fastjson.JSONObject body = jsonObject.getJSONObject("body");
        if (!"1".equals(body.getString("retCode")))
            throw new XkdException("查询余额失败");
        BalanceVo balanceVo = new BalanceVo();
        balanceVo.setBalance(body.getBigDecimal("balance"));
        BigDecimal totalBalance = companyMapper.getTotalBaofuBalance();//已分配金额
        BigDecimal subtract = balanceVo.getBalance().subtract(totalBalance);//可分配金额
        balanceVo.setTowUndistributedAmount(subtract);
        return balanceVo;
    }

    @Override
    public void ABolishBind(String uniqueCode) {
        try {
            String ProtocolNo = uniqueCode;//签约协议号（确认绑卡返回）

            String MemberId = in_member_id;//商户号
            String TerminalId = in_terminal_id;//终端号

            String pfxpath = baopriPath;//商户私钥
            String cerpath = baofucerPath;//宝付公钥
            String pfxpwd = pfxPathPassword;//私钥密码

            String send_time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//报文发送日期时间
            String AesKey = FormatUtil.CreateAeskey();//商户自定义(可随机生成  AES key长度为=16位)
            String dgtl_envlp = "01|" + AesKey;//使用接收方的公钥加密后的对称密钥，并做Base64转码，明文01|对称密钥，01代表AES[密码商户自定义]
            log.info("密码dgtl_envlp：" + dgtl_envlp);
            dgtl_envlp = RsaCodingUtil.encryptByPubCerFile(SecurityUtil.Base64Encode(dgtl_envlp), cerpath);//公钥加密

            log.info("签约协议号：" + ProtocolNo);
            ProtocolNo = SecurityUtil.AesEncrypt(SecurityUtil.Base64Encode(ProtocolNo), AesKey);//先BASE64后进行AES加密
            log.info("签约协议号AES结果:" + ProtocolNo);

            Map<String, String> DateArry = new TreeMap<String, String>();
            DateArry.put("send_time", send_time);
            DateArry.put("msg_id", "TISN" + FormatUtil.CreateAeskey(28));//报文流水号
            DateArry.put("version", "4.0.0.0");
            DateArry.put("terminal_id", TerminalId);
            DateArry.put("txn_type", "04");//交易类型
            DateArry.put("member_id", MemberId);
            DateArry.put("dgtl_envlp", dgtl_envlp);
            DateArry.put("user_id", "");//用户在商户平台唯一ID (和绑卡时要一致)
            DateArry.put("protocol_no", ProtocolNo);//签约协议号（密文）

            String SignVStr = FormatUtil.coverMap2String(DateArry);
            log.info("SHA-1摘要字串：" + SignVStr);
            String signature = SecurityUtil.sha1X16(SignVStr, "UTF-8");//签名
            log.info("SHA-1摘要结果：" + signature);
            String Sign = SignatureUtils.encryptByRSA(signature, pfxpath, pfxpwd);
            log.info("RSA签名结果：" + Sign);
            DateArry.put("signature", Sign);//签名域

            String PostString = HttpUtil.RequestForm("https://public.baofoo.com/cutpayment/protocol/backTransRequest", DateArry);
            log.info("请求返回:" + PostString);

            Map<String, String> ReturnData = FormatUtil.getParm(PostString);

            if (!ReturnData.containsKey("signature")) {
                throw new Exception("缺少验签参数！");
            }

            String RSign = ReturnData.get("signature");
            log.info("返回的验签值：" + RSign);
            ReturnData.remove("signature");//需要删除签名字段
            String RSignVStr = FormatUtil.coverMap2String(ReturnData);
            log.info("返回SHA-1摘要字串：" + RSignVStr);
            String RSignature = SecurityUtil.sha1X16(RSignVStr, "UTF-8");//签名
            log.info("返回SHA-1摘要结果：" + RSignature);

            if (SignatureUtils.verifySignature(cerpath, RSignature, RSign)) {
                log.info("Yes");//验签成功
            }
            if (!ReturnData.containsKey("resp_code")) {
                throw new Exception("缺少resp_code参数！");
            }
            if (ReturnData.get("resp_code").toString().equals("S")) {
                log.info("解绑成功！");
            } else if (ReturnData.get("resp_code").toString().equals("F")) {
                log.info("解绑失败！");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void checkBalanceAndFreeze(Integer companyId) {
        Company company = companyMapper.selectById(companyId);
        if (!company.getAccountingType().equals(SysConstant.ACCOUNTING_TYPE_ONE)) {
            Integer blocId = company.getBlocId();
            BigDecimal bigDecimal = companyMapper.selectBalance(blocId);
            if (bigDecimal.compareTo(BigDecimal.ZERO) < 0)
                throw new XkdException("服务费不足，无法提现");
        } else {
            BigDecimal bigDecimal = companyMapper.selectBalance(companyId);
            if (bigDecimal.compareTo(BigDecimal.ZERO) < 0)
                throw new XkdException("服务费不足，无法提现");
        }
        if ("1".equals(company.getIsFreeze())) {
            throw new XkdException("账户已被冻结");
        }
    }

    @Override
    public void towBaofuwithdrawDeposit(TowWithdrawDepositDto towWithdrawDepositDto) throws Exception {
        //测试环境
        if (!active.equals("prd")) {
            testTowBaofuwithdrawDeposit(towWithdrawDepositDto);
            return;
        }
        log.info("执行渠道二提现");
        //判断服务费是否为负
        checkBalanceAndFreeze(towWithdrawDepositDto.getCompanyId());
        String amount = towWithdrawDepositDto.getAmount();//元
        long penny = new BigDecimal(amount).multiply(new BigDecimal(100)).longValue();//分
        String lockName = towBaofuwithdrawLock + "PT";
        RLock lock = redissonClient.getLock(lockName);
        try {
            log.info("尝试获取锁: {}", lockName);
            boolean b = lock.tryLock(10, TimeUnit.MILLISECONDS);
            if (!b) {
                log.info("获取锁失败: {}", lockName);
                throw new Exception("服务器繁忙！");
            }
            log.info("成功获取锁: {}", lockName);
            //查询未分配余额
            BalanceVo balanceVo = baofuBalance(SysConstant.CHANNEL_TYPE_TWO);//查询宝付总余额
            BigDecimal totalBalance = companyMapper.getTotalBaofuBalance();//已分配金额
            BigDecimal subtract = balanceVo.getBalance().subtract(totalBalance);//可分配金额
            if (subtract.compareTo(new BigDecimal(amount)) < 0)
                throw new XkdException("可分配金额不足");
            String dataType = TransConstant.data_type_json; // 数据类型 xml/json
            TransContent<TransReqBF0040001> transContent = new TransContent<TransReqBF0040001>(dataType);
            List<TransReqBF0040001> trans_reqDatas = new ArrayList<TransReqBF0040001>();
            TransReqBF0040001 transReqData = new TransReqBF0040001();
            String order = OrderNumUtil.getOutAmountOrder();
            transReqData.setTrans_no(order);
            transReqData.setTrans_money(amount);//元
            transReqData.setTo_acc_name(towWithdrawDepositDto.getToAccNameBaofu());
            transReqData.setTo_acc_no(towWithdrawDepositDto.getToAccNoBaofu());
            transReqData.setTo_bank_name(towWithdrawDepositDto.getToBankNameBoafu());
            transReqData.setTrans_cnap(towWithdrawDepositDto.getTransCnapBaofu());
            trans_reqDatas.add(transReqData);
            transContent.setTrans_reqDatas(trans_reqDatas);

            log.info("订单号：" + transReqData.getTrans_no());
            String bean2XmlString = transContent.obj2Str(transContent);
            log.info("报文：" + bean2XmlString);

            String origData = bean2XmlString;

            origData = SecurityUtil.Base64Encode(origData);
//        String DataContent = RsaCodingUtil.encryptByPriPfxFile(origData, companyAccount.getOutPriPath(), AESUtil.decrypt(companyAccount.getOutPriPw(), passKey));
            String DataContent = RsaCodingUtil.encryptByPriPfxFile(origData, baopriPath, pfxPathPassword);
            log.info("----------->【私钥加密-结果】" + DataContent);

            String member_id = tow_out_member_id;
            String terminal_id = tow_out_terminal_id;
            //发送请求参数
            Map<String, String> PostArry = new TreeMap<String, String>();
            PostArry.put("version", "4.0.0");
            PostArry.put("member_id", member_id);
            PostArry.put("terminal_id", terminal_id);
            PostArry.put("data_type", dataType);
            PostArry.put("data_content", DataContent);

            String ReslutString = HttpUtil.RequestForm(baseUrl0040001, PostArry);
            log.info("请求返回:" + ReslutString);
            TransContent<TransRespBF0040001> str2Obj = new TransContent<TransRespBF0040001>(dataType);

            //明文返回处理可能是报文头参数不正确、或其他的异常导致；
            if (ReslutString.contains("trans_content")) {
                //明文返回
                //我报文错误处理
                str2Obj = (TransContent<TransRespBF0040001>) str2Obj.str2Obj(ReslutString, TransRespBF0040001.class);
                // 业务逻辑判断
            } else {
                //密文返回
                //第一步：公钥解密
//            ReslutString = RsaCodingUtil.decryptByPubCerFile(ReslutString, companyAccount.getOutPubPath());
                ReslutString = RsaCodingUtil.decryptByPubCerFile(ReslutString, baofucerPath);
                log.info("解密RSA：" + ReslutString);
                //第二步BASE64解密
                ReslutString = SecurityUtil.Base64Decode(ReslutString);
                log.info("解密后明文:" + ReslutString);
                str2Obj = (TransContent<TransRespBF0040001>) str2Obj.str2Obj(ReslutString, TransRespBF0040001.class);
                // 业务逻辑判断
            }
            String returnCode = str2Obj.getTrans_head().getReturn_code();
            String returnMsg = str2Obj.getTrans_head().getReturn_msg();
            if (!returnCode.equals("0000")) {
                throw new XkdException("渠道二提现失败：" + returnMsg);
            }
            //受理成功 入库
            ChannelPayTow channelPayTow = new ChannelPayTow();
            channelPayTow.setOrderNo(order);
            channelPayTow.setPayType("2");
            channelPayTow.setAmount(penny);
            channelPayTow.setAccountType(towWithdrawDepositDto.getType());
            channelPayTow.setPayStatus("1");
            channelPayTow.setCompanyId(SysConstant.SUPER_COMPANY_ID);
            channelPayTow.setCreateTime(new Date());
            channelPayTow.setRemark(towWithdrawDepositDto.getRemark());
            channelPayTow.setToAccName(towWithdrawDepositDto.getToAccNameBaofu());
            channelPayTow.setToAccNo(towWithdrawDepositDto.getToAccNoBaofu());
            channelPayTow.setToBankName(towWithdrawDepositDto.getToBankNameBoafu());
            channelPayTow.setTransCnapBaofu(towWithdrawDepositDto.getTransCnapBaofu());
            channelPayTowMapper.insert(channelPayTow);
            log.info("渠道二提现执行完毕");
        } catch (Exception e) {
            log.error("渠道二提现，请求参数: {}, 锁键: {}, 异常信息: {}", towWithdrawDepositDto, lockName, e.getMessage(), e);
            throw e;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("渠道二提现任务释放锁，锁键: {}", lockName);
            }
        }

    }

    private void testTowBaofuwithdrawDeposit(TowWithdrawDepositDto towWithdrawDepositDto) {
        String order = OrderNumUtil.getOutAmountOrder();
        String amount = towWithdrawDepositDto.getAmount();//元
        long penny = new BigDecimal(amount).multiply(new BigDecimal(100)).longValue();//分

        //受理成功 入库
        ChannelPayTow channelPayTow = new ChannelPayTow();
        channelPayTow.setOrderNo(order);
        channelPayTow.setPayType("2");
        channelPayTow.setAmount(penny);
        channelPayTow.setAccountType(towWithdrawDepositDto.getType());
        channelPayTow.setPayStatus("2");
        channelPayTow.setCompanyId(SysConstant.SUPER_COMPANY_ID);
        channelPayTow.setCreateTime(new Date());
        channelPayTow.setToAccName(towWithdrawDepositDto.getToAccNameBaofu());
        channelPayTow.setToAccNo(towWithdrawDepositDto.getToAccNoBaofu());
        channelPayTow.setToBankName(towWithdrawDepositDto.getToBankNameBoafu());
        channelPayTow.setRemark(towWithdrawDepositDto.getRemark());
        channelPayTow.setTransCnapBaofu(towWithdrawDepositDto.getTransCnapBaofu());
        channelPayTow.setTradeTime(new Date());
        channelPayTowMapper.insert(channelPayTow);

        if (channelPayTow.getAccountType().equals("1")) {//放款账户
            companyMapper.updateTowBaofuBalance(channelPayTow.getCompanyId(), channelPayTow.getAmount());
        } else if (channelPayTow.getAccountType().equals("2")) {//回款账户
            companyMapper.updateTowReturnBalance(channelPayTow.getCompanyId(), channelPayTow.getAmount());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void towBaofuwithdrawDepositNotify(Map<String, Object> fpnre) throws IOException {
        log.info("执行放款回调函数");
        if (fpnre == null)
            throw new XkdException("Parms is Null");
        log.info("访问入参：" + fpnre);
        String resultJson = JSON.toJSONString(fpnre);
        FoPayNotifyRsaEntity result = JSON.parseObject(resultJson, FoPayNotifyRsaEntity.class);
        log.info("转对象：" + result);
        if (result == null) throw new XkdException("对象转换异常");
        if (result.getData_content() == null) throw new XkdException("数据域为空");
        if (result.getData_content().equals("null") || StringUtils.isBlank(result.getData_content()))
            throw new XkdException("数据域为空");
        String ReslutString = RsaCodingUtil.decryptByPubCerFile(result.getData_content(), baofucerPath);
        log.info("解密RSA：" + ReslutString);
        //第二步BASE64解密
        ReslutString = SecurityUtil.Base64Decode(ReslutString);
        log.info("BASE64解密:" + ReslutString);
        /***
         * 商户端处理
         */
        JSONObject jsonObject = XML.toJSONObject(ReslutString);
        JSONObject trans_content = jsonObject.getJSONObject("trans_content");
        JSONObject trans_reqDatas = trans_content.getJSONObject("trans_reqDatas");
        JSONObject trans_reqData = trans_reqDatas.getJSONObject("trans_reqData");
        String transOrderid = trans_reqData.getStr("trans_orderid");
        log.info("代付（提现）回调宝付订单号：" + transOrderid);
        String transNo = trans_reqData.getStr("trans_no");//提现商户订单
        String state = trans_reqData.getStr("state");
        String trans_remark = trans_reqData.getStr("trans_remark");
        String trans_endtime = trans_reqData.getStr("trans_endtime");
        log.info("代付（提现）回调商户订单号：" + transNo);

        ChannelPayTow channelPayTow = null;
        for (int i = 1; i <= maxRetry; i++) {
            channelPayTow = channelPayTowMapper.selectOne(new LambdaQueryWrapper<ChannelPayTow>().eq(ChannelPayTow::getOrderNo, transNo));
            log.info("第 {} 次查询渠道二代付订单数据：{}", i, channelPayTow);
            if (channelPayTow != null)
                break;
            try {
                Thread.sleep(sleepMillis);
            } catch (InterruptedException e) {
                log.warn("等待过程中被中断", e);
                Thread.currentThread().interrupt(); // 恢复中断状态
                break;
            }
        }
        if (channelPayTow == null) {
            log.error("重试 {} 次后仍未获取到渠道二代付订单数据", maxRetry);
            throw new RuntimeException("未获取到渠道二代付订单数据，请稍后再试");
        }

        if (!channelPayTow.getPayStatus().equals("1")) {//如果不是申请中的状态直接返回 避免重复回调
            return;
        }

        String status = null;
        String incomingStatus = "";
        Integer incomingId = channelPayTow.getIncomingId();
        IncomingInfo incomingInfo = null;
        Integer productId = null;
        Product product = null;
        if (incomingId != null) {
            incomingInfo = incomingInfoMapper.selectById(incomingId);
            productId = incomingInfo.getProductId();
            product = productMapper.selectById(productId);
            ;
        }
        OutInfo outInfo = new OutInfo();
        List<RepaymentSchedule> schedules = new ArrayList<>();
        if (state.equals("1")) {//成功
            incomingStatus = SysConstant.BUSINESS_STATUS_THIRTEEN;
            status = "2";
            if ("1".equals(channelPayTow.getPayType())) {
                Credit credit = creditMapper.selectByCredit(incomingId);
                outInfo.setPayTime(new Date());
                outInfo.setOrderNo(transNo);
                OutInfo outInfo1 = outInfoMapper.selectById(channelPayTow.getOutId());
                //保单贷：：入保单
                if (product.getType().equals(SysConstant.PRODUCT_FIVE)) {
                    iOutInfoService.calculateInsuranceLoanSchedule(outInfo1,credit.getPostServiceFeeRate());
                    List<CarCredit> carCredits = carCreditMapper.selectList(new LambdaQueryWrapper<CarCredit>()
                            .eq(CarCredit::getIncomingId, incomingId)
                            .ge(CarCredit::getEndTime, new Date()));
                    List<GuaranteeSlip> guaranteeSlips = BeanUtil.copyToList(carCredits, GuaranteeSlip.class);
                    if (CollectionUtil.isNotEmpty(guaranteeSlips)) {
                        guaranteeSlipMapper.batchInsert(guaranteeSlips);
                    }
                } else {
                    schedules = iOutInfoService.calculateLoanSchedule(outInfo1);
                }
                String incomingOrder = incomingInfo.getIncomingOrder();
                //扣除合同服务费
                iCompanyService.deductingBalanceMain(null, null, incomingId, null, SysConstant.BUSINESS_FEE_SEVEN, null, null, null);
                //扣除放款服务费
                iCompanyService.deductingBalanceMain(outInfo1, incomingOrder, incomingId, null, SysConstant.BUSINESS_FEE_FIVE, null, null, null);
                try {
                    //扣除产品服务费
                    iCompanyService.deductingBalanceMain(outInfo1, null, incomingId, null, SysConstant.BUSINESS_FEE_ONE, null, null, null);
                } catch (Exception e) {
                    log.info("放款回调，放款金额未在设置阶梯收费范围内");
                }
                //发送短信

                String format = sendText(outInfo1, incomingInfo.getName(), credit);
                try {
                    if (!product.getType().equals("4")) {
                        log.info("发送放款到账短信");
                        msmService.sendH5(AESUtil.decrypt(incomingInfo.getPhone(), aesKey), format, format);
                    }
                } catch (Exception e) {
                    log.info("发送放款到账短信失败");
                }

            }

            //修改宝付余额
            if (channelPayTow.getAccountType().equals("1")) {//放款账户
                companyMapper.updateTowBaofuBalance(channelPayTow.getCompanyId(), channelPayTow.getAmount());
            }
//            else if (channelPayTow.getAccountType().equals("2")) {//回款账户
//                companyMapper.updateTowReturnBalance(channelPayTow.getCompanyId(), channelPayTow.getAmount());
//            }
        } else if (state.equals("-1")) {//支付失败
            status = "3";
            incomingStatus = SysConstant.BUSINESS_STATUS_TWELVE;
        } else if (state.equals("2")) {//退款
            status = "4";
            incomingStatus = SysConstant.BUSINESS_STATUS_TWELVE;
        }
        if ("1".equals(channelPayTow.getPayType())) {
            if (product.getType().equals(SysConstant.PRODUCT_FIVE)) {
                incomingInfoMapper.updateStatus(incomingStatus, incomingId);
            } else {
                outInfo.setStatus(incomingStatus);
            }
            //修改放款表状态
            outInfo.setOutStatus(status);
            outInfo.setTransOrderId(transOrderid);
            LambdaQueryWrapper<OutInfo> lqw = new LambdaQueryWrapper<>();
            lqw.eq(OutInfo::getOutId, channelPayTow.getOutId());
            //修改放款状态
            outInfoMapper.update(outInfo, lqw);
            //修改冻结金额
            companyMapper.freezeBaofuBalance(channelPayTow.getCompanyId(), -channelPayTow.getAmount());
            //如果是第三方的数据 回调对方
            if (incomingInfo.getIncomingType().equals("2"))
                iAsyncService.advanceOutAmount(channelPayTow.getOrderNo(), channelPayTow.getCompanyId(), status, trans_remark, JSONUtil.toJsonStr(schedules));
        }
        ChannelPayTow o = new ChannelPayTow();
        o.setPayStatus(status);
        o.setFailReason(trans_remark);
        o.setTradeTime(DateUtil.parse(trans_endtime));
        LambdaQueryWrapper<ChannelPayTow> lqw2 = new LambdaQueryWrapper<>();
        lqw2.eq(ChannelPayTow::getOrderNo, transNo);
        channelPayTowMapper.update(o, lqw2);

        log.info("渠道二代付回调函数执行完毕");
    }

    @Override
    public void advancePayment(AdvancePaymentDto advancePaymentDto) throws Exception {

        Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, advancePaymentDto.getIncomingId()));
        String TransId = OrderNumUtil.getRepayOrder();
        long multiply = credit.getAdvanceAmount().multiply(new BigDecimal(100)).longValue();
        AdvancePaymentOrder old = advancePaymentOrderMapper.selectOne(new LambdaQueryWrapper<AdvancePaymentOrder>().
                eq(AdvancePaymentOrder::getIncomingId, advancePaymentDto.getIncomingId()).
                in(AdvancePaymentOrder::getPayStatus, "1", "2"));
        if (old != null)
//            throw new XkdException("请勿重复申请预付款");
            return;

        AdvancePaymentOrder advancePaymentOrder = new AdvancePaymentOrder();
        advancePaymentOrder.setOrderNo(TransId);
        advancePaymentOrder.setAmount(multiply);
        advancePaymentOrder.setType(advancePaymentDto.getType());
        advancePaymentOrder.setCompanyId(credit.getCompanyId());
        advancePaymentOrder.setCreateTime(new Date());
        advancePaymentOrder.setRepayName(credit.getRepayName());
        advancePaymentOrder.setRepayAccount(credit.getRepayAccount());
        advancePaymentOrder.setRepayBank(credit.getRepayBank());
        advancePaymentOrder.setIncomingId(advancePaymentDto.getIncomingId());
        advancePaymentOrder.setOutId(advancePaymentDto.getOutId());
        if (advancePaymentDto.getType().equals("0")) {//银行转账
            advancePaymentOrderMapper.insert(advancePaymentOrder);
            return;
        }

        CompanyAccount companyAccount = companyAccountMapper.selectById(credit.getCompanyId());
        String send_time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//报文发送日期时间 //send_time="2038-12-23 10:00:00";
        CardProtocolInfo cardProtocolInfo = cardProtocolInfoMapper.selectOne(new LambdaQueryWrapper<CardProtocolInfo>().eq(CardProtocolInfo::getRepayAccount, credit.getRepayAccount()));
        if (cardProtocolInfo == null)
            throw new XkdException("已解绑银行卡");
        String ProtocolNo = cardProtocolInfo.getProtocolNo();//签约协议号（确认绑卡返回）
        OutInfo outInfo = outInfoMapper.selectById(advancePaymentDto.getOutId());
        String payPass = outInfo.getPayPass();
        String MemberId = null;
        String TerminalId = null;
        String txn_type = null;
        if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {
            MemberId = in_member_id;//商户号
            TerminalId = in_terminal_id;
            txn_type = "18";//分账
        } else if (payPass.equals(SysConstant.CHANNEL_TYPE_TWO)) {
            MemberId = tow_in_member_id;//商户号
            TerminalId = tow_in_terminal_id;
            txn_type = "08";//不分账
        }

        String pfxpath = baopriPath;//商户私钥
        String cerpath = baofucerPath;//宝付公钥
        String pfxpwd = pfxPathPassword;//私钥密码


        log.info("订单号：" + TransId);

        String AesKey = FormatUtil.CreateAeskey();//商户自定义(可随机生成  AES key长度为=16位)
        String dgtl_envlp = "01|" + AesKey;//+AesKey;//使用接收方的公钥加密后的对称密钥，并做Base64转码，明文01|对称密钥，01代表AES[密码商户自定义]
        log.info("密码dgtl_envlp：" + dgtl_envlp);
        dgtl_envlp = RsaCodingUtil.encryptByPubCerFile(SecurityUtil.Base64Encode(dgtl_envlp), cerpath);//公钥加密
        log.info("RSA公钥加密结果" + dgtl_envlp);


        ProtocolNo = SecurityUtil.AesEncrypt(SecurityUtil.Base64Encode(ProtocolNo), AesKey);//先BASE64后进行AES加密
        log.info("签约协议号AES结果:" + ProtocolNo);

        String CardInfo = "";//信用卡：信用卡有效期|安全码,借记卡：传空

        //暂不支持信用卡
        CardInfo = SecurityUtil.AesEncrypt(SecurityUtil.Base64Encode(CardInfo), AesKey);//先BASE64后进行AES加密


        Map<String, String> DateArry = new TreeMap<String, String>();
        DateArry.put("send_time", send_time);
        DateArry.put("msg_id", "FMXC" + FormatUtil.CreateAeskey(28));//报文流水号
        DateArry.put("version", "4.0.0.0");
        DateArry.put("terminal_id", TerminalId);
        DateArry.put("txn_type", txn_type);//交易类型(参看：文档中《交易类型枚举》)
        DateArry.put("member_id", MemberId);
        DateArry.put("trans_id", TransId);
        DateArry.put("dgtl_envlp", dgtl_envlp);
        DateArry.put("user_id", "");//用户在商户平台唯一ID (和绑卡时要一致)
        DateArry.put("protocol_no", ProtocolNo);//签约协议号（密文）
        DateArry.put("txn_amt", String.valueOf(multiply));//交易金额 [单位：分  例：1元则提交100]，此处注意数据类型的转转，建议使用BigDecimal类弄进行转换为字串
        DateArry.put("card_info", CardInfo);//卡信息
        if (payPass.equals(SysConstant.CHANNEL_TYPE_ONE)) {
            DateArry.put("share_notify_url", baodansharenotify);//分账异步通知
            //100%分账
            String share_info = companyAccount.getPrestoreMemberId() + "," + multiply + ";";
            DateArry.put("share_info", share_info);//分账
        }
//		DateArry.put("fee_member_id", "");//手续费商户

        Map<String, String> RiskItem = new HashMap<String, String>();
        /**--------风控基础参数-------------**/
        /**
         * 说明风控参数必须，按商户开通行业、真实交易信息传，不可传固定值。
         */
        RiskItem.put("goodsCategory", "02");//商品类目 详见附录《商品类目》 平台为互消金融
        RiskItem.put("chPayIp", "127.0.0.1");//持卡人支付IP
        RiskItem.put("deviceOrderNo", "");//加载设备指纹中的订单号


        DateArry.put("risk_item", net.sf.json.JSONObject.fromObject(RiskItem).toString());//放入风控参数
        DateArry.put("return_url", baodansinglePayReturn);//最多填写三个地址,不同地址用间使用‘|’分隔 //异步通知接收地址

        String SignVStr = FormatUtil.coverMap2String(DateArry);
        log.info("SHA-1摘要字串：" + SignVStr);
        String signature = SecurityUtil.sha1X16(SignVStr, "UTF-8");//签名
        log.info("SHA-1摘要结果：" + signature);
        String Sign = SignatureUtils.encryptByRSA(signature, pfxpath, pfxpwd);
        log.info("RSA签名结果：" + Sign);
        DateArry.put("signature", Sign);//签名域

        String PostString = HttpUtil.RequestForm(xieyiurl, DateArry);
        log.info("请求返回:" + PostString);

        Map<String, String> ReturnData = FormatUtil.getParm(PostString);

        if (!ReturnData.containsKey("signature")) {
            throw new XkdException("缺少验签参数！");
        }
        String RSign = ReturnData.get("signature");
        log.info("返回的验签值：" + RSign);
        ReturnData.remove("signature");//需要删除签名字段
        String RSignVStr = FormatUtil.coverMap2String(ReturnData);
        log.info("返回SHA-1摘要字串：" + RSignVStr);
        String RSignature = SecurityUtil.sha1X16(RSignVStr, "UTF-8");//签名
        log.info("返回SHA-1摘要结果：" + RSignature);

        if (SignatureUtils.verifySignature(cerpath, RSignature, RSign)) {
            log.info("验签：Yes");//验签成功

            if (!ReturnData.containsKey("resp_code")) {
                throw new XkdException("缺少resp_code参数！");
            }
            if (ReturnData.get("resp_code").toString().equals("S")) {
                log.info("申请支付成功！");
            } else if (ReturnData.get("resp_code").toString().equals("I")) {
                log.info("交易处理中！");
            } else if (ReturnData.get("resp_code").toString().equals("F")) {
                advancePaymentOrder.setPayStatus("3");
                advancePaymentOrder.setFailCause(ReturnData.get("biz_resp_msg").toString());
            } else {
                throw new XkdException("反回异常！");//异常不得做为订单状态。
            }
            advancePaymentOrderMapper.insert(advancePaymentOrder);

        } else {
            log.info("验签：NO");
        }
    }


}
